]> de.git.xonotic.org Git - xonotic/darkplaces.git/commitdiff
build: minor adjustments master
authorbones_was_here <bones_was_here@xonotic.au>
Thu, 11 Apr 2024 17:55:08 +0000 (03:55 +1000)
committerbones_was_here <bones_was_here@xonotic.au>
Sun, 14 Apr 2024 16:24:08 +0000 (02:24 +1000)
VS2019: get more of the targets to build (needed to specify modern C
standard for all of them, and SDK "10.0" seems to mean "use latest
version installed").
VS2019: improve README.md instructions.

Makefile: fix PEDANTIC=1 test builds and warning spam.

sys: properly fix warnings related to SDL's wrapping of main() for all
platforms.

Signed-off-by: bones_was_here <bones_was_here@xonotic.au>
150 files changed:
.gitattributes
.github/workflows/build.yml [new file with mode: 0644]
.gitignore
CONTRIBUTING.md [new file with mode: 0644]
COPYING
Darkplaces.app/Contents/Info.plist
Doxyfile
README.md
SDLMain.h [deleted file]
SDLMain.m [deleted file]
builddate.c
cap_ogg.c
cd_shared.c
cl_cmd.c
cl_collision.c
cl_demo.c
cl_input.c
cl_main.c
cl_parse.c
cl_particles.c
cl_particles.h
cl_screen.c
cl_screen.h
cl_video.c
cl_video_jamdecode.c
cl_video_libavw.c
client.h
clvm_cmds.c
cmd.c
cmd.h
collision.h
com_game.c
com_infostring.c
com_infostring.h
com_msg.c
common.c
common.h
console.c
console.h
convex.c [new file with mode: 0644]
convex.h [new file with mode: 0644]
crypto.c
crypto.h
csprogs.c
csprogs.h
cvar.c
cvar.h
darkplaces-sdl2-vs2017.vcxproj [deleted file]
darkplaces-sdl2-vs2019.vcxproj
darkplaces-vs2017.sln [deleted file]
darkplaces.h
dpdefs/csprogsdefs.qc
dpdefs/dpextensions.qc
dpdefs/menudefs.qc
dpdefs/progsdefs.qc
dpvsimpledecode.c
draw.h
filematch.c
fs.c
fs.h
ft2.c
ft2.h
ft2_fontdefs.h
gl_backend.c
gl_draw.c
gl_rmain.c
gl_rsurf.c
gl_textures.c
glquake.h
host.c
host.h
image.c
jpeg.c
keys.c
keys.h
lhnet.c
lhnet.h
libcurl.c
libcurl.h
makefile
makefile.inc
mathlib.h
matrixlib.c
matrixlib.h
mdfour.c
menu.c
menu.h
model_alias.c
model_brush.c
model_shared.c
model_shared.h
model_sprite.c
mvm_cmds.c
netconn.c [changed mode: 0755->0644]
netconn.h
packages.config
phys.c [new file with mode: 0644]
phys.h [new file with mode: 0644]
progsvm.h
protocol.c
protocol.h
prvm_cmds.c
prvm_cmds.h
prvm_edict.c
prvm_exec.c
prvm_execprogram.h
prvm_offsets.h
qdefs.h
quakedef.h
r_modules.c
r_shadow.c
r_sky.c
r_stats.c
render.h
sbar.c
sbar.h
screen.h
server.h
snd_main.c
snd_main.h
snd_mix.c
sv_ccmds.c
sv_demo.c
sv_ents.c
sv_main.c
sv_phys.c
sv_save.c
sv_send.c
sv_user.c
svvm_cmds.c
sys.h
sys_null.c [new file with mode: 0644]
sys_sdl.c
sys_shared.c
sys_unix.c [deleted file]
taskqueue.c
todo
utf8lib.c
utf8lib.h
vid.h
vid_null.c
vid_sdl.c
vid_shared.c
view.c
vs2012_sdl2_win32.props [deleted file]
vs2012_sdl2_win64.props [deleted file]
world.c
world.h
zone.c
zone.h

index c8716cc00e4e11ae2a536addca590515c27f8f19..7e8260ee271ee66f1360ae10d8c0ee771291b834 100644 (file)
@@ -165,9 +165,9 @@ POSITIONS -diff -crlf
 *.psd -diff -crlf
 *.py crlf=input
 *.q3map1 crlf=input
-*.qc crlf=input
+*.qc crlf=input linguist-language=C
 *.qdt crlf=input
-*.qh crlf=input
+*.qh crlf=input linguist-language=C
 *.rb crlf=input
 *.rc2 crlf=input
 *.rc -crlf
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644 (file)
index 0000000..27fbd2d
--- /dev/null
@@ -0,0 +1,45 @@
+name: CI
+
+on:
+  # Run on pushes to tags, the "master" branch, and PR's
+  push:
+    branches:
+      - master
+  pull_request:
+
+jobs:
+  build:
+
+    runs-on: ubuntu-latest
+    container:
+      image: debian:latest
+    steps:
+      # Must install git before checking out the repo otherwise github doesn't fetch the .git directory.
+      - name: Install dependencies
+        run: |
+          apt update
+          apt install --yes build-essential git libjpeg-dev libsdl2-dev libcurl4-openssl-dev libpng-dev libfreetype-dev libvorbis-dev
+
+      - name: Fetch repository
+        uses: actions/checkout@v4.1.1
+        with:
+          # make `git describe` show the correct commit hash
+          fetch-depth: '0'
+
+      - name: Compile DP
+        run: |
+          # prevent git complaining about dubious ownership of the repo
+          chown -R root:root .
+          # fail if `git describe` doesn't work (required for the buildstring)
+          git describe --always
+          # fail if there's any warnings
+          export CC="cc -Werror"
+          make sdl-release
+
+      - name: Upload Linux artifacts
+        uses: actions/upload-artifact@v4
+        with:
+          name: Linux
+          path: |
+            darkplaces-sdl
+
index 360f4f995ddb09eb425983a8d99696284b96b966..4c4c53c9aedc82d5ecf50e147a54518cae8cee0f 100644 (file)
@@ -3,6 +3,16 @@ obj/
 *.o
 *.i
 *.s
+
+# MSVC build objects
+Debug-darkplaces-sdl2-vs*/
+Release-darkplaces-sdl2-vs*/
+*.obj
+*.tlog
+# MSVC NuGet packages, VS downloads them automatically.
+# Other platforms also provide SDL2 headers rather than storing old ones here.
+packages/
+
 ChangeLog
 darkplaces-agl
 darkplaces-glx
@@ -24,3 +34,10 @@ Makefile.win
 .ccls-cache
 *.gch
 /.vs
+/build
+/.cache
+*.kdev4
+*.vcxproj.user
+*.pdb
+*.lib
+*.exp
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644 (file)
index 0000000..56beb08
--- /dev/null
@@ -0,0 +1,120 @@
+# DarkPlaces Contributing Guidelines
+-------------------------------------------------------------------------------
+
+1. ### Do not break Quake or its mods, and any other game using the engine.
+
+   The engine has known compatibility issues with Quake and many community
+   mods. All code must not make the situation worse. This is analogous to the policy
+   of the Linux kernel to not break userspace.
+
+2. ### Sign off all of your commits if they are to be included upstream.
+
+   You must use a valid, permanent email address.
+
+2. ### All code submitted should follow the Allman style for the most part.
+
+       1. In statements, the curly brace should be placed on the next line at the
+          same indentation level as the statement. If the statement only involves
+          a single line, do not use curly braces.
+
+               ```c
+               // Example:
+               if(foo == 1)
+               {
+                       Do_Something();
+                       Do_Something_Else();
+               }
+               else
+                       Do_Something_Else_Else();
+
+               if(bar == 1)
+                       Do_Something_Else_Else_Else();
+               ```
+
+       2. Use tabs for indentation.
+
+          Use spaces subsequently for alignment of the
+          parameters of multi-line function calls or declarations, or statements.
+
+               ```c
+               // Example:
+               if(very_long_statement &&
+                  very_long_statement_aligned &&
+                  foo)
+               {
+                       if(indented)
+                       {
+                               Do_Something();
+                               Do_Something_Else();
+                       }
+               }
+               ```
+
+       3. If possible, try to keep individual lines of code less than 100
+          characters.
+
+       4. Pointer operators should be placed on the right-hand side.
+
+               ```c
+               int foo = 1;
+               int *bar = &foo;
+               ```
+
+       5. Type casts should have a space between the type and the pointer.
+
+               ```c
+               int *foo = (int *)malloc(5);
+               ```
+
+       6. Use spaces after each comma when listing parameters or variables of a
+          struct.
+
+       7. Multi-line comments should be formatted like so:
+
+               ```c
+               /*
+                * This is a multi-line comment.
+                * Sometimes, I dream about cheese.
+                */
+               ```
+
+          But this is okay too:
+
+               ```c
+               /* This is another multi-line comment.
+                * Hiya! How are you?
+                */
+               ```
+
+       8. If following these guidelines in any manner would make any code less
+          readable or harder to follow, ***ignore them***. This section is only
+          guidelines, not rules. We're not gonna beat you over the head in pull
+          requests because you misplaced a dereference operator.
+
+          For example, in some situations, placing the block on the same line as
+          the condition would be okay because it looks cleaner:
+
+               ```c
+               if (foo)  Do_Something();
+               if (bar)  Do_Something_Else();
+               if (far)  near();
+               if (boo)  AHH("!!!\n");
+               ```
+
+4. DarkPlaces is written in a special subset of C and C++ that sits in the
+   center of the Venn diagram of compatibility between the two languages.
+   While there is no practical reason for keeping it this way (yet), it is
+   generally preferred that all code submitted at least compiles under gcc and
+   g++ and clang(++). This could be done by setting the CC environment
+   variable to g++ or clang++ on the command-line before building.
+
+   Most of the differences are enforced by a compile warning set to be an
+   error (`-Werror=c++-compat`) but some are subtle and would cause behavior
+   differences between the two compilers, or are not caught by that warning.
+   The things to look out for are:
+
+       1. Character constants are `int`-sized in C but `char`-sized in C++. This
+       means `sizeof('a')` will be 4 in C, but 1 in C++.
+
+       2. `goto label;` cannot jump over a variable initialization. This will
+       cause a compiler error as C++ but is not caught by `-Wc++-compat`.
diff --git a/COPYING b/COPYING
index d60c31a97a544b53039088d14fe9114583c0efc3..e90dfed1a31ed1c0c22befce22c6f37f9cf5f2bb 100644 (file)
--- a/COPYING
+++ b/COPYING
@@ -55,7 +55,7 @@ patent must be licensed for everyone's free use or not licensed at all.
 
   The precise terms and conditions for copying, distribution and
 modification follow.
-\f
+
                    GNU GENERAL PUBLIC LICENSE
    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
 
@@ -110,7 +110,7 @@ above, provided that you also meet all of these conditions:
     License.  (Exception: if the Program itself is interactive but
     does not normally print such an announcement, your work based on
     the Program is not required to print an announcement.)
-\f
+
 These requirements apply to the modified work as a whole.  If
 identifiable sections of that work are not derived from the Program,
 and can be reasonably considered independent and separate works in
@@ -168,7 +168,7 @@ access to copy from a designated place, then offering equivalent
 access to copy the source code from the same place counts as
 distribution of the source code, even though third parties are not
 compelled to copy the source along with the object code.
-\f
+
   4. You may not copy, modify, sublicense, or distribute the Program
 except as expressly provided under this License.  Any attempt
 otherwise to copy, modify, sublicense or distribute the Program is
@@ -225,7 +225,7 @@ impose that choice.
 
 This section is intended to make thoroughly clear what is believed to
 be a consequence of the rest of this License.
-\f
+
   8. If the distribution and/or use of the Program is restricted in
 certain countries either by patents or by copyrighted interfaces, the
 original copyright holder who places the Program under this License
@@ -278,7 +278,7 @@ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
 POSSIBILITY OF SUCH DAMAGES.
 
                     END OF TERMS AND CONDITIONS
-\f
+
            How to Apply These Terms to Your New Programs
 
   If you develop a new program, and you want it to be of the greatest
index b2aa11d6172f12f59605db72be7d6c020dea8d20..f6331a15dda2cc262b674039adc18110f41625fb 100644 (file)
        <string>????</string>
        <key>CFBundleVersion</key>
        <string>1.0</string>
+       <key>LSApplicationCategoryType</key>
+       <string>public.app-category.action-games</string>
+       <key>LSMinimumSystemVersion</key>
+       <string>10.4.0</string>
+       <key>LSMinimumSystemVersionByArchitecture</key>
+       <dict>
+               <key>x86_64</key>
+               <string>10.6.0</string>
+       </dict>
 </dict>
 </plist>
index 322052e075230ad310bc068e9d3acbf675f61bc8..5ab1e3b6a2e841012a1e523184141bc7313ad18d 100644 (file)
--- a/Doxyfile
+++ b/Doxyfile
-# Doxyfile 1.5.9
+# Doxyfile 1.9.2
 
 # This file describes the settings to be used by the documentation system
-# doxygen (www.doxygen.org) for a project
+# doxygen (www.doxygen.org) for a project.
 #
-# All text after a hash (#) is considered a comment and will be ignored
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
 # The format is:
-#       TAG = value [value, ...]
-# For lists items can also be appended using:
-#       TAG += value [value, ...]
-# Values that contain spaces should be placed between quotes (" ")
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
 
 #---------------------------------------------------------------------------
 # 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
 
-# The PROJECT_NAME tag is a single word (or a sequence of words surrounded 
-# by quotes) that should identify the project.
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
+
+PROJECT_NAME           = "DarkPlaces"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
+
+PROJECT_NUMBER         =
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
 
-PROJECT_NAME           = darkplaces
+PROJECT_BRIEF          = "Game engine based on the Quake 1 engine by id Software, developed by LadyHavoc"
 
-# The PROJECT_NUMBER tag can be used to enter a project or revision number. 
-# This could be handy for archiving the generated documentation or 
-# if some version control system is used.
+# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
+# in the documentation. The maximum height of the logo should not exceed 55
+# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
+# the logo to the output directory.
 
-PROJECT_NUMBER         = 
+PROJECT_LOGO           = "darkplaces48x48.png"
 
-# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) 
-# base path where the generated documentation will be put. 
-# If a relative path is entered, it will be relative to the location 
-# where doxygen was started. If left blank the current directory will be used.
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
 
-OUTPUT_DIRECTORY       = docs
+OUTPUT_DIRECTORY       =
 
-# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 
-# 4096 sub-directories (in 2 levels) under the output directory of each output 
-# format and will distribute the generated files over these directories. 
-# Enabling this option can be useful when feeding doxygen a huge amount of 
-# source files, where putting all generated files in the same directory would 
-# otherwise cause performance problems for the file system.
+# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
 
 CREATE_SUBDIRS         = NO
 
-# The OUTPUT_LANGUAGE tag is used to specify the language in which all 
-# documentation generated by doxygen is written. Doxygen will use this 
-# information to generate all constant output in the proper language. 
-# The default language is English, other supported languages are: 
-# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, 
-# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, 
-# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English 
-# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, 
-# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, 
-# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
+# characters to appear in the names of generated files. If set to NO, non-ASCII
+# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
+# U+3044.
+# The default value is: NO.
+
+ALLOW_UNICODE_NAMES    = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
 
 OUTPUT_LANGUAGE        = English
 
-# If the BRIEF_MEMBER_DESC tag is set to YES (the default) 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.
+# 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.
+# The default value is: YES.
 
 BRIEF_MEMBER_DESC      = YES
 
-# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend 
-# the brief description of a member or function before the detailed description. 
-# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the 
+# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
 # brief descriptions will be completely suppressed.
+# The default value is: YES.
 
 REPEAT_BRIEF           = YES
 
-# This tag implements a quasi-intelligent brief description abbreviator 
-# that is used to form the text in various listings. Each string 
-# in this list, if found as the leading text of the brief description, will be 
-# stripped from the text and the result after processing the whole list, is 
-# used as the annotated text. Otherwise, the brief description is used as-is. 
-# If left blank, the following values are used ("$name" is automatically 
-# replaced with the name of the entity): "The $name class" "The $name widget" 
-# "The $name file" "is" "provides" "specifies" "contains" 
-# "represents" "a" "an" "the"
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
 
-ABBREVIATE_BRIEF       = 
+ABBREVIATE_BRIEF       = "The $name class" "The $name widget" "The $name file" is provides specifies contains represents a an the
 
-# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then 
-# Doxygen will generate a detailed section even if there is only a brief 
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
 # description.
+# The default value is: NO.
 
-ALWAYS_DETAILED_SEC    = NO
+ALWAYS_DETAILED_SEC    = YES
 
-# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all 
-# inherited members of a class in the documentation of that class as if those 
-# members were ordinary class members. Constructors, destructors and assignment 
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
 # operators of the base classes will not be shown.
+# The default value is: NO.
 
-INLINE_INHERITED_MEMB  = NO
+INLINE_INHERITED_MEMB  = YES
 
-# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full 
-# path before files name in the file list and in the header files. If set 
-# to NO the shortest path that makes the file name unique will be used.
+# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
 
 FULL_PATH_NAMES        = YES
 
-# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag 
-# can be used to strip a user-defined part of the path. Stripping is 
-# only done if one of the specified strings matches the left-hand part of 
-# the path. The tag can be used to show relative paths in the file list. 
-# If left blank the directory from which doxygen is run is used as the 
-# path to strip.
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
 
-STRIP_FROM_PATH        = 
+STRIP_FROM_PATH        =
 
-# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of 
-# the path mentioned in the documentation of a class, which tells 
-# the reader which header file to include in order to use a class. 
-# If left blank only the name of the header file containing the class 
-# definition is used. Otherwise one should specify the include paths that 
-# are normally passed to the compiler using the -I flag.
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
 
-STRIP_FROM_INC_PATH    = 
+STRIP_FROM_INC_PATH    =
 
-# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter 
-# (but less readable) file names. This can be useful is your file systems 
-# doesn't support long names like on DOS, Mac, or CD-ROM.
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
 
 SHORT_NAMES            = NO
 
-# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen 
-# will interpret the first line (until the first dot) of a JavaDoc-style 
-# comment as the brief description. If set to NO, the JavaDoc 
-# comments will behave just like regular Qt-style comments 
-# (thus requiring an explicit @brief command for a brief description.)
-
-JAVADOC_AUTOBRIEF      = 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 comments 
-# will behave just like regular Qt-style comments (thus requiring 
-# an explicit \brief command for a brief description.)
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: 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
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
+
+QT_AUTOBRIEF           = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
 
-QT_AUTOBRIEF           = YES
+MULTILINE_CPP_IS_BRIEF = NO
 
-# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen 
-# treat a multi-line C++ special comment block (i.e. a block of //! or /// 
-# comments) as a brief description. This used to be the default behaviour. 
-# The new default is to treat a multi-line C++ comment block as a detailed 
-# description. Set this tag to YES if you prefer the old behaviour instead.
+# By default Python docstrings are displayed as preformatted text and doxygen's
+# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the
+# doxygen's special commands can be used and the contents of the docstring
+# documentation blocks is shown as doxygen documentation.
+# The default value is: YES.
 
-MULTILINE_CPP_IS_BRIEF = NO
+PYTHON_DOCSTRING       = YES
 
-# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented 
-# member inherits the documentation from any documented member that it 
-# re-implements.
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
 
 INHERIT_DOCS           = YES
 
-# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce 
-# a new page for each member. If set to NO, the documentation of a member will 
-# be part of the file/class/namespace that contains it.
+# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new
+# page for each member. If set to NO, the documentation of a member will be part
+# of the file/class/namespace that contains it.
+# The default value is: NO.
 
 SEPARATE_MEMBER_PAGES  = NO
 
-# The TAB_SIZE tag can be used to set the number of spaces in a tab. 
-# Doxygen uses this value to replace tabs by spaces in code fragments.
-
-TAB_SIZE               = 8
-
-# This tag can be used to specify a number of aliases that acts 
-# as commands in the documentation. An alias has the form "name=value". 
-# For example adding "sideeffect=\par Side Effects:\n" 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.
-
-ALIASES                = 
-
-# 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 members will be omitted, etc.
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE               = 4
+
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:^^"
+# 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:". Note that you cannot put \n's in the value part of an alias
+# to insert 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                =
+
+# 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
+# members will be omitted, etc.
+# The default value is: NO.
 
 OPTIMIZE_OUTPUT_FOR_C  = YES
 
-# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java 
-# sources only. Doxygen will then generate output that is more tailored for 
-# Java. For instance, namespaces will be presented as packages, qualified 
-# scopes will look different, etc.
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
 
 OPTIMIZE_OUTPUT_JAVA   = NO
 
-# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran 
-# sources only. Doxygen will then generate output that is more tailored for 
-# Fortran.
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
 
 OPTIMIZE_FOR_FORTRAN   = NO
 
-# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL 
-# sources. Doxygen will then generate output that is tailored for 
-# VHDL.
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
 
 OPTIMIZE_OUTPUT_VHDL   = 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, VHDL, C, C++. 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 that for custom extensions you also need to set
-# FILE_PATTERNS otherwise the files are not read by doxygen.
-
-EXTENSION_MAPPING      = 
-
-# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want 
-# to include (a tag file for) the STL sources as input, then you should 
-# set this tag to YES in order to let doxygen match functions declarations and 
-# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. 
-# func(std::string) {}). This also make the inheritance and collaboration 
+# 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,
+# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice,
+# VHDL, 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). 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.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen. When specifying no_extension you should add
+# * to the FILE_PATTERNS.
+#
+# Note see also the list of default file extension mappings.
+
+EXTENSION_MAPPING      = qc=C qh=C inc=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 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.
+# The default value is: YES.
+
+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
+# globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT       = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
 # diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
 
 BUILTIN_STL_SUPPORT    = NO
 
-# If you use Microsoft's C++/CLI language, you should set this option to YES to 
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
 # enable parsing support.
+# The default value is: NO.
 
 CPP_CLI_SUPPORT        = NO
 
-# Set the SIP_SUPPORT tag to YES if your project consists of sip 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.
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# 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.
 
 SIP_SUPPORT            = NO
 
-# For Microsoft's IDL there are propget and propput attributes to indicate getter 
-# and setter methods for a property. Setting this option to YES (the default) 
-# will make doxygen to replace the get and set methods by a property in the 
-# documentation. This will only work if the methods are indeed getting or 
-# setting a simple type. If this is not the case, or you want to show the 
-# methods anyway, you should set this option to NO.
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
 
 IDL_PROPERTY_SUPPORT   = YES
 
-# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC 
-# tag is set to YES, then doxygen will reuse the documentation of the first 
-# member in the group (if any) for the other members of the group. By default 
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
 # all members of a group must be documented explicitly.
+# The default value is: NO.
 
 DISTRIBUTE_GROUP_DOC   = NO
 
-# Set the SUBGROUPING tag to YES (the default) 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 subgrouping. Alternatively, this can be done per class using 
-# the \nosubgrouping command.
+# 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.
 
-SUBGROUPING            = YES
+GROUP_NESTED_COMPOUNDS = NO
 
-# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum 
-# is documented as struct, union, or enum with the name of the typedef. So 
-# typedef struct TypeS {} TypeT, will appear in the documentation as a struct 
-# with name TypeT. When disabled the typedef will appear as a member of a file, 
-# namespace, or class. And the struct will be named TypeS. This can typically 
-# be useful for C code in case the coding convention dictates that all compound 
-# types are typedef'ed and only the typedef is referenced, never the tag name.
-
-TYPEDEF_HIDES_STRUCT   = NO
+# 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
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
 
-# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to 
-# determine which symbols to keep in memory and which to flush to disk. 
-# When the cache is full, less often used symbols will be written to disk. 
-# For small to medium size projects (<1000 input files) the default value is 
-# probably good enough. For larger projects a too small cache size can cause 
-# doxygen to be busy swapping symbols to and from disk most of the time 
-# causing a significant performance penality. 
-# If the system has enough physical memory increasing the cache will improve the 
-# performance by keeping more symbols in memory. Note that the value works on 
-# a logarithmic scale so increasing the size by one will rougly double the 
-# memory usage. The cache size is given by this formula: 
-# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, 
-# corresponding to a cache size of 2^16 = 65536 symbols
+SUBGROUPING            = YES
 
-SYMBOL_CACHE_SIZE      = 0
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS  = NO
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT   = YES
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE      = 0
+
+# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use
+# during processing. When set to 0 doxygen will based this on the number of
+# cores available in the system. You can set it explicitly to a value larger
+# than 0 to get more control over the balance between CPU load and processing
+# speed. At this moment only the input processing can be done using multiple
+# threads. Since this is still an experimental feature the default is set to 1,
+# which effectively disables parallel processing. Please report any issues you
+# encounter. Generating dot graphs in parallel is controlled by the
+# DOT_NUM_THREADS setting.
+# Minimum value: 0, maximum value: 32, default value: 1.
+
+NUM_PROC_THREADS       = 1
 
 #---------------------------------------------------------------------------
 # Build related configuration options
 #---------------------------------------------------------------------------
 
-# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in 
-# documentation are documented, even if no documentation was available. 
-# Private class members and static file members will be hidden unless 
-# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
 
 EXTRACT_ALL            = YES
 
-# If the EXTRACT_PRIVATE tag is set to YES all private members of a class 
-# will be included in the documentation.
+# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
 
-EXTRACT_PRIVATE        = NO
+EXTRACT_PRIVATE        = YES
 
-# If the EXTRACT_STATIC tag is set to YES all static members of a file 
-# will be included in the documentation.
+# 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_STATIC         = NO
+EXTRACT_PRIV_VIRTUAL   = YES
 
-# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) 
-# defined locally in source files will be included in the documentation. 
-# If set to NO only classes defined in header files are included.
+# 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.
+
+EXTRACT_PACKAGE        = YES
+
+# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+EXTRACT_STATIC         = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO,
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
 
 EXTRACT_LOCAL_CLASSES  = YES
 
-# This flag is only useful for Objective-C code. When 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 (the default) only methods in the interface are included.
+# 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.
+# The default value is: NO.
+
+EXTRACT_LOCAL_METHODS  = YES
 
-EXTRACT_LOCAL_METHODS  = NO
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
 
-# If this flag is set to YES, the members of anonymous namespaces will be 
-# extracted and appear in the documentation as a namespace called 
-# 'anonymous_namespace{file}', where file will be replaced with the base 
-# name of the file that contains the anonymous namespace. By default 
-# anonymous namespace are hidden.
+EXTRACT_ANON_NSPACES   = YES
 
-EXTRACT_ANON_NSPACES   = NO
+# If this flag is set to YES, the name of an unnamed parameter in a declaration
+# will be determined by the corresponding definition. By default unnamed
+# parameters remain unnamed in the output.
+# The default value is: YES.
 
-# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all 
-# undocumented members of documented classes, files or namespaces. 
-# If set to NO (the default) these members will be included in the 
-# various overviews, but no documentation section is generated. 
-# This option has no effect if EXTRACT_ALL is enabled.
+RESOLVE_UNNAMED_PARAMS = YES
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
 
 HIDE_UNDOC_MEMBERS     = NO
 
-# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all 
-# undocumented classes that are normally visible in the class hierarchy. 
-# If set to NO (the default) these classes will be included in the various 
-# overviews. This option has no effect if EXTRACT_ALL is enabled.
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO, these classes will be included in the various overviews. This option
+# has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
 
 HIDE_UNDOC_CLASSES     = NO
 
-# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all 
-# friend (class|struct|union) declarations. 
-# If set to NO (the default) these declarations will be included in the 
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# declarations. If set to NO, these declarations will be included in the
 # documentation.
+# The default value is: NO.
 
 HIDE_FRIEND_COMPOUNDS  = NO
 
-# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any 
-# documentation blocks found inside the body of a function. 
-# If set to NO (the default) these blocks will be appended to the 
-# function's detailed documentation block.
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO, these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
 
 HIDE_IN_BODY_DOCS      = NO
 
-# The INTERNAL_DOCS tag determines if documentation 
-# that is typed after a \internal command is included. If the tag is set 
-# to NO (the default) then the documentation will be excluded. 
-# Set it to YES to include the internal documentation.
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS          = YES
+
+# With the correct setting of option CASE_SENSE_NAMES doxygen will better be
+# able to match the capabilities of the underlying filesystem. In case the
+# filesystem is case sensitive (i.e. it supports files in the same directory
+# whose names only differ in casing), the option must be set to YES to properly
+# deal with such files in case they appear in the input. For filesystems that
+# are not case sensitive the option should be be set to NO to properly deal with
+# output files written for symbols that only differ in casing, such as for two
+# classes, one named CLASS and the other named Class, and to also support
+# references to files without having to specify the exact matching casing. On
+# Windows (including Cygwin) and MacOS, users should typically set this option
+# to NO, whereas on Linux or other Unix flavors it should typically be set to
+# YES.
+# The default value is: system dependent.
 
-INTERNAL_DOCS          = NO
+CASE_SENSE_NAMES       = YES
 
-# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate 
-# file 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.
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES, the
+# scope will be hidden.
+# The default value is: NO.
 
-CASE_SENSE_NAMES       = YES
+HIDE_SCOPE_NAMES       = NO
+
+# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will
+# append additional text to a page's title, such as Class Reference. If set to
+# YES the compound reference will be hidden.
+# The default value is: NO.
+
+HIDE_COMPOUND_REFERENCE= NO
 
-# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen 
-# will show members with their full class and namespace scopes in the 
-# documentation. If set to YES the scope will be hidden.
+# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class
+# will show which file needs to be included to use the class.
+# The default value is: YES.
 
-HIDE_SCOPE_NAMES       = YES
+SHOW_HEADERFILE        = YES
 
-# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen 
-# will put a list of the files that are included by a file in the documentation 
-# of that file.
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
 
 SHOW_INCLUDE_FILES     = YES
 
-# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] 
-# is inserted in the documentation for inline members.
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC  = YES
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES   = NO
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
 
 INLINE_INFO            = YES
 
-# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen 
-# will sort the (detailed) documentation of file and class members 
-# alphabetically by member name. If set to NO the members will appear in 
-# declaration order.
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order.
+# The default value is: YES.
 
 SORT_MEMBER_DOCS       = YES
 
-# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the 
-# brief documentation of file, namespace and class members alphabetically 
-# by member name. If set to NO (the default) the members will appear in 
-# declaration order.
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
 
-SORT_BRIEF_DOCS        = NO
+SORT_BRIEF_DOCS        = YES
 
-# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the 
-# hierarchy of group names into alphabetical order. If set to NO (the default) 
-# the group names will appear in their defined order.
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
+
+SORT_MEMBERS_CTORS_1ST = YES
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
 
 SORT_GROUP_NAMES       = NO
 
-# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be 
-# sorted by fully-qualified names, including namespaces. If set to 
-# NO (the default), the class list will be sorted only by class name, 
-# not including the namespace part. 
-# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. 
-# Note: This option applies only to the class list, not to the 
-# alphabetical list.
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
 
 SORT_BY_SCOPE_NAME     = NO
 
-# The GENERATE_TODOLIST tag can be used to enable (YES) or 
-# disable (NO) the todo list. This list is created by putting \todo 
-# commands in the documentation.
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING  = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo
+# list. This list is created by putting \todo commands in the documentation.
+# The default value is: YES.
 
 GENERATE_TODOLIST      = YES
 
-# The GENERATE_TESTLIST tag can be used to enable (YES) or 
-# disable (NO) the test list. This list is created by putting \test 
-# commands in the documentation.
+# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test
+# list. This list is created by putting \test commands in the documentation.
+# The default value is: YES.
 
 GENERATE_TESTLIST      = YES
 
-# The GENERATE_BUGLIST tag can be used to enable (YES) or 
-# disable (NO) the bug list. This list is created by putting \bug 
-# commands in the documentation.
+# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
 
 GENERATE_BUGLIST       = YES
 
-# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or 
-# disable (NO) the deprecated list. This list is created by putting 
-# \deprecated commands in the documentation.
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
 
 GENERATE_DEPRECATEDLIST= YES
 
-# The ENABLED_SECTIONS tag can be used to enable conditional 
-# documentation sections, marked by \if sectionname ... \endif.
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
 
-ENABLED_SECTIONS       = 
+ENABLED_SECTIONS       =
 
-# The MAX_INITIALIZER_LINES tag determines the maximum number of lines 
-# the initial value of a variable or define consists of for it to appear in 
-# the documentation. If the initializer consists of more lines than specified 
-# here it will be hidden. Use a value of 0 to hide initializers completely. 
-# The appearance of the initializer of individual variables and defines in the 
-# documentation can be controlled using \showinitializer or \hideinitializer 
-# command in the documentation regardless of this setting.
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
 
 MAX_INITIALIZER_LINES  = 30
 
-# If the sources in your project are distributed over multiple directories 
-# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy 
-# in the documentation. The default is NO.
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES, the
+# list will mention the files that were used to generate the documentation.
+# The default value is: YES.
 
-SHOW_DIRECTORIES       = NO
+SHOW_USED_FILES        = YES
 
-# Set the SHOW_FILES tag to NO to disable the generation of the Files page. 
-# This will remove the Files entry from the Quick Index and from the 
-# Folder Tree View (if specified). The default is YES.
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
 
 SHOW_FILES             = YES
 
-# Set the SHOW_NAMESPACES tag to NO to disable the generation of the 
-# Namespaces page.  This will remove the Namespaces entry from the Quick Index 
-# and from the Folder Tree View (if specified). The default is YES.
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
 
 SHOW_NAMESPACES        = YES
 
-# The FILE_VERSION_FILTER tag can be used to specify a program or script that 
-# doxygen should invoke to get the current version for each file (typically from 
-# the version control system). Doxygen will invoke the program by executing (via 
-# popen()) the command <command> <input-file>, where <command> is the value of 
-# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file 
-# provided by doxygen. Whatever the program writes to standard output 
-# is used as the file version. See the manual for examples.
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+FILE_VERSION_FILTER    =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file. See also section "Changing the
+# layout of pages" for information.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
 
-FILE_VERSION_FILTER    = 
+LAYOUT_FILE            =
 
-# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by 
-# doxygen. The layout file controls the global structure of the generated output files 
-# in an output format independent way. The create the layout file that represents 
-# doxygen's defaults, run doxygen with the -l option. You can optionally specify a 
-# file name after the option, if omitted DoxygenLayout.xml will be used as the name 
-# of the 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 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.
 
-LAYOUT_FILE            = 
+CITE_BIB_FILES         =
 
 #---------------------------------------------------------------------------
-# configuration options related to warning and progress messages
+# Configuration options related to warning and progress messages
 #---------------------------------------------------------------------------
 
-# The QUIET tag can be used to turn on/off the messages that are generated 
-# by doxygen. Possible values are YES and NO. If left blank NO is used.
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
 
 QUIET                  = NO
 
-# The WARNINGS tag can be used to turn on/off the warning messages that are 
-# generated by doxygen. Possible values are YES and NO. If left blank 
-# NO is used.
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
 
 WARNINGS               = YES
 
-# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings 
-# for undocumented members. If EXTRACT_ALL is set to YES then this flag will 
-# automatically be disabled.
+# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
 
 WARN_IF_UNDOCUMENTED   = YES
 
-# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for 
-# potential errors in the documentation, such as not documenting some 
-# parameters in a documented function, or documenting parameters that 
-# don't exist or using markup commands wrongly.
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as documenting some parameters in
+# a documented function twice, or documenting parameters that don't exist or
+# using markup commands wrongly.
+# The default value is: YES.
 
 WARN_IF_DOC_ERROR      = YES
 
-# This WARN_NO_PARAMDOC option can be abled to get warnings for 
-# functions that are documented, but have no documentation for their parameters 
-# or return value. If set to NO (the default) doxygen will only warn about 
-# wrong or incomplete parameter documentation, but not about the absence of 
-# documentation.
+# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete
+# function parameter documentation. If set to NO, doxygen will accept that some
+# parameters have no documentation without warning.
+# The default value is: YES.
+
+WARN_IF_INCOMPLETE_DOC = 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 parameter
+# documentation, but not about the absence of documentation. If EXTRACT_ALL is
+# set to YES then this flag will automatically be disabled. See also
+# WARN_IF_INCOMPLETE_DOC
+# The default value is: NO.
 
 WARN_NO_PARAMDOC       = 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 and the warning text. Optionally the format may contain 
-# $version, which will be replaced by the version of the file (if it could 
-# be obtained via FILE_VERSION_FILTER)
+# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
+# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS
+# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but
+# at the end of the doxygen process doxygen will return with a non-zero status.
+# Possible values are: NO, YES and FAIL_ON_WARNINGS.
+# 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
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
 
 WARN_FORMAT            = "$file:$line: $text"
 
-# The WARN_LOGFILE tag can be used to specify a file to which warning 
-# and error messages should be written. If left blank the output is written 
-# to stderr.
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
 
-WARN_LOGFILE           = 
+WARN_LOGFILE           =
 
 #---------------------------------------------------------------------------
-# configuration options related to the input files
+# Configuration options related to the input files
 #---------------------------------------------------------------------------
 
-# The INPUT tag can be 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.
+# 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. See also FILE_PATTERNS and EXTENSION_MAPPING
+# Note: If this tag is empty the current directory is searched.
 
-INPUT                  = .
+INPUT                  =
 
-# This tag can be used to specify the character encoding of the source files 
-# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is 
-# also the default input encoding. 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 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:
+# https://www.gnu.org/software/libiconv/) for the list of possible encodings.
+# The default value is: UTF-8.
 
 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 pattern (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 *.h *.hh *.hxx 
-# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
+# 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.
+#
+# 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.
+#
+# Note the list of default checked file patterns might differ from the list of
+# default file extension mappings.
+#
+# 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++, *.l, *.cs, *.d, *.php, *.php4, *.php5, *.phtml,
+# *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C
+# comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd,
+# *.vhdl, *.ucf, *.qsf and *.ice.
 
-FILE_PATTERNS          = 
+FILE_PATTERNS          = *.c *.cc *.cxx *.cpp *.c++ *.h *.hh *.hxx *.hpp *.h++ *.qc *.qh *.inc *.md
 
-# The RECURSIVE tag can be used to turn specify whether or not subdirectories 
-# should be searched for input files as well. Possible values are YES and NO. 
-# If left blank NO is used.
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
 
 RECURSIVE              = YES
 
-# The EXCLUDE tag can be used to specify files and/or directories that should 
-# excluded from the INPUT source files. This way you can easily exclude a 
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
 # subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
 
-EXCLUDE                = 
+EXCLUDE                =
 
-# The EXCLUDE_SYMLINKS tag can be used select whether or not files or 
-# directories that are symbolic links (a Unix filesystem feature) are excluded 
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
 # from the input.
+# The default value is: NO.
 
 EXCLUDE_SYMLINKS       = NO
 
-# If the value of the INPUT tag contains directories, you can use the 
-# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude 
-# certain files from those directories. Note that the wildcards are matched 
-# against the file with absolute path, so to exclude all test directories 
-# for example use the pattern */test/*
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
 
-EXCLUDE_PATTERNS       = 
+EXCLUDE_PATTERNS       =
 
-# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names 
-# (namespaces, classes, functions, etc.) that should be excluded from the 
-# output. The symbol name can be a fully qualified name, a word, or if the 
-# wildcard * is used, a substring. Examples: ANamespace, AClass, 
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
 # AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
 
-EXCLUDE_SYMBOLS        = 
+EXCLUDE_SYMBOLS        =
 
-# The EXAMPLE_PATH tag can be used to specify one or more files or 
-# directories that contain example code fragments that are included (see 
-# the \include command).
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
 
-EXAMPLE_PATH           = 
+EXAMPLE_PATH           =
 
-# If the value of the EXAMPLE_PATH tag contains directories, you can use the 
-# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
-# and *.h) to filter out the source-files in the directories. If left 
-# blank all files are included.
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
 
-EXAMPLE_PATTERNS       = 
+EXAMPLE_PATTERNS       =
 
-# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be 
-# searched for input files to be used with the \include or \dontinclude 
-# commands irrespective of the value of the RECURSIVE tag. 
-# Possible values are YES and NO. If left blank NO is used.
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
 
 EXAMPLE_RECURSIVE      = NO
 
-# The IMAGE_PATH tag can be used to specify one or more files or 
-# directories that contain image that are included in the documentation (see 
-# the \image command).
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
 
-IMAGE_PATH             = 
+IMAGE_PATH             =
 
-# 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 
-# by executing (via popen()) the command <filter> <input-file>, where <filter> 
-# is the value of the INPUT_FILTER tag, and <input-file> is the name of an 
-# input file. Doxygen will then use the output that the filter program writes 
-# to standard output.  If FILTER_PATTERNS is specified, this tag will be 
-# ignored.
+# 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
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# 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           =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=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.
 
-INPUT_FILTER           = 
+FILTER_PATTERNS        =
 
-# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern 
-# basis.  Doxygen will compare the file name with each pattern and apply the 
-# filter if there is a match.  The filters are a list of the form: 
-# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further 
-# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER 
-# is applied to all files.
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
 
-FILTER_PATTERNS        = 
+FILTER_SOURCE_FILES    = NO
 
-# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using 
-# INPUT_FILTER) will be used to filter the input files when producing source 
-# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
 
-FILTER_SOURCE_FILES    = NO
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE = README.md
 
 #---------------------------------------------------------------------------
-# configuration options related to source browsing
+# Configuration options related to source browsing
 #---------------------------------------------------------------------------
 
-# If the SOURCE_BROWSER tag is set to YES then a list of source files will 
-# be generated. Documented entities will be cross-referenced with these sources. 
-# Note: To get rid of all source code in the generated output, make sure also 
-# VERBATIM_HEADERS is set to NO.
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
 
 SOURCE_BROWSER         = YES
 
-# Setting the INLINE_SOURCES tag to YES will include the body 
-# of functions and classes directly in the documentation.
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
 
-INLINE_SOURCES         = NO
+INLINE_SOURCES         = YES
 
-# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct 
-# doxygen to hide any special comment blocks from generated source code 
-# fragments. Normal C and C++ comments will always remain visible.
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
 
 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.
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# entity all documented functions referencing it will be listed.
+# The default value is: NO.
 
-REFERENCED_BY_RELATION = NO
+REFERENCED_BY_RELATION = YES
 
-# If the REFERENCES_RELATION tag is set to YES 
-# then for each documented function all documented entities 
-# called/used by that function will be listed.
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
 
-REFERENCES_RELATION    = NO
+REFERENCES_RELATION    = YES
 
-# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) 
-# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from 
-# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will 
-# link to the source code.  Otherwise they will link to the documentation.
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
 
 REFERENCES_LINK_SOURCE = 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 4.8.6 or higher.
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+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 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 configuration file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
 
 USE_HTAGS              = NO
 
-# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen 
-# will generate a verbatim copy of the header file for each class for 
-# which an include is specified. Set to NO to disable this.
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
 
 VERBATIM_HEADERS       = YES
 
+# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the
+# clang parser (see:
+# http://clang.llvm.org/) for more accurate parsing at the cost of reduced
+# performance. This can be particularly helpful with template 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
+# generated with the -Duse_libclang=ON option for CMake.
+# The default value is: NO.
+
+CLANG_ASSISTED_PARSING = NO
+
+# If the CLANG_ASSISTED_PARSING tag is set to YES and the CLANG_ADD_INC_PATHS
+# tag is set to YES then doxygen will add the directory of each input to the
+# include path.
+# The default value is: YES.
+# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
+
+CLANG_ADD_INC_PATHS    = YES
+
+# If clang assisted parsing is enabled you can provide the compiler with command
+# line options that you would normally use when invoking the compiler. Note that
+# the include paths will already be set by doxygen for the files and directories
+# specified with INPUT and INCLUDE_PATH.
+# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
+
+CLANG_OPTIONS          =
+
+# If clang assisted parsing is enabled you can provide the clang parser with the
+# path to the directory containing a file called compile_commands.json. This
+# file is the compilation database (see:
+# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the
+# options used when the source 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. Any options specified with CLANG_OPTIONS
+# will be added as well.
+# 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
+# Configuration options related to the alphabetical class index
 #---------------------------------------------------------------------------
 
-# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index 
-# of all compounds will be generated. Enable this if the project 
-# contains a lot of classes, structs, unions or interfaces.
-
-ALPHABETICAL_INDEX     = NO
-
-# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then 
-# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns 
-# in which this list will be split (can be a number in the range [1..20])
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
 
-COLS_IN_ALPHA_INDEX    = 5
+ALPHABETICAL_INDEX     = YES
 
-# 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 one or more prefixes that 
-# should be ignored while generating the index headers.
+# 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
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
 
-IGNORE_PREFIX          = 
+IGNORE_PREFIX          =
 
 #---------------------------------------------------------------------------
-# configuration options related to the HTML output
+# Configuration options related to the HTML output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_HTML tag is set to YES (the default) Doxygen will 
-# generate HTML output.
+# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
+# The default value is: YES.
 
 GENERATE_HTML          = YES
 
-# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. 
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
-# put in front of it. If left blank `html' will be used as the default path.
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
 HTML_OUTPUT            = html
 
-# The HTML_FILE_EXTENSION tag can be used to specify the file extension for 
-# each generated HTML page (for example: .htm,.php,.asp). If it is left blank 
-# doxygen will generate files with .html extension.
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
 HTML_FILE_EXTENSION    = .html
 
-# The HTML_HEADER tag can be used to specify a personal HTML header for 
-# each generated HTML page. If it is left blank doxygen will generate a 
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
 # standard header.
-
-HTML_HEADER            = 
-
-# The HTML_FOOTER tag can be used to specify a personal HTML footer for 
-# each generated HTML page. If it is left blank doxygen will generate a 
-# standard footer.
-
-HTML_FOOTER            = 
-
-# The HTML_STYLESHEET tag can be used to specify a user-defined cascading 
-# style sheet that is used by each HTML page. It can be used to 
-# fine-tune the look of the HTML output. If the tag is left blank doxygen 
-# will generate a default style sheet. Note that doxygen will try to copy 
-# the style sheet file to the HTML output directory, so don't put your own 
-# stylesheet in the HTML output directory as well, or it will be erased!
-
-HTML_STYLESHEET        = 
-
-# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, 
-# files or namespaces will be aligned in HTML using tables. If set to 
-# NO a bullet list will be used.
-
-HTML_ALIGN_MEMBERS     = 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. For this to work a browser that supports 
-# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox 
-# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
-
-HTML_DYNAMIC_SECTIONS  = NO
-
-# 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, 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.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER            =
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FOOTER            =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET        =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# cascading style sheets that are included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefore more robust against future updates.
+# Doxygen will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list). For an example see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET  =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+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 color-wheel, see
+# 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.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_HUE    = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use gray-scales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT    = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA  = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to YES can help to show when doxygen was last run and thus if the
+# documentation is up to date.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP         = YES
+
+# 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.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS  = YES
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+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:
+# 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 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.
 
 GENERATE_DOCSET        = NO
 
-# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the 
-# feed. A documentation feed provides an umbrella under which multiple 
-# documentation sets from a single provider (such as a company or product suite) 
-# can be grouped.
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
 
 DOCSET_FEEDNAME        = "Doxygen generated docs"
 
-# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that 
-# should uniquely identify the documentation set bundle. This should be a 
-# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen 
-# will append .docset to the name.
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
 
 DOCSET_BUNDLE_ID       = org.doxygen.Project
 
-# If the GENERATE_HTMLHELP tag is set to YES, additional index files 
-# will be generated that can be used as input for tools like the 
-# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) 
-# of the generated HTML documentation.
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+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
+# on Windows. In the beginning of 2021 Microsoft took the original page, with
+# a.o. the download links, offline the HTML help workshop was already many years
+# in maintenance mode). You can download the HTML help workshop from the web
+# archives at Installation executable (see:
+# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo
+# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe).
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
 GENERATE_HTMLHELP      = NO
 
-# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can 
-# be used to specify the file name of the resulting .chm file. You 
-# can add a path in front of the file if the result should not be 
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
 # written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
 
-CHM_FILE               = 
+CHM_FILE               =
 
-# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can 
-# be used to specify the location (absolute path including file name) of 
-# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run 
-# the HTML help compiler on the generated index.hhp.
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler (hhc.exe). If non-empty,
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
 
-HHC_LOCATION           = 
+HHC_LOCATION           =
 
-# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag 
-# controls if a separate .chi index file is generated (YES) or that 
-# it should be included in the master .chm file (NO).
+# The GENERATE_CHI flag controls if a separate .chi index file is generated
+# (YES) or that it should be included in the main .chm file (NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
 
 GENERATE_CHI           = NO
 
-# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING 
-# is used to encode HtmlHelp index (hhk), content (hhc) and project file 
-# content.
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
 
-CHM_INDEX_ENCODING     = 
+CHM_INDEX_ENCODING     =
 
-# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag 
-# controls whether a binary table of contents is generated (YES) or a 
-# normal table of contents (NO) in the .chm file.
+# The BINARY_TOC flag controls whether a binary table of contents is generated
+# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it
+# enables the Previous and Next buttons.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
 
 BINARY_TOC             = NO
 
-# The TOC_EXPAND flag can be set to YES to add extra items for group members 
-# to the contents of the HTML help documentation and to the tree view.
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
 
 TOC_EXPAND             = NO
 
-# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER 
-# are set, an additional index file will be generated that can be used as input for 
-# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated 
-# HTML documentation.
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
 GENERATE_QHP           = NO
 
-# If the QHG_LOCATION tag is specified, the QCH_FILE tag can 
-# be used to specify the file name of the resulting .qch file. 
-# The path specified is relative to the HTML output folder.
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
 
-QCH_FILE               = 
+QCH_FILE               =
 
-# The QHP_NAMESPACE tag specifies the namespace to use when generating 
-# Qt Help Project output. For more information please see 
-# http://doc.trolltech.com/qthelpproject.html#namespace
+# 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:
+# 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.
 
-QHP_NAMESPACE          = 
+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 
-# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+# 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:
+# 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.
 
 QHP_VIRTUAL_FOLDER     = doc
 
-# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add. 
-# For more information please see 
-# http://doc.trolltech.com/qthelpproject.html#custom-filters
+# 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:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+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:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+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:
+# 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  =
+
+# The QHG_LOCATION tag can be used to specify the location (absolute path
+# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to
+# run qhelpgenerator on the generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION           =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP   = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID         = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX          = YES
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine tune the look of the index (see "Fine-tuning the output"). As an
+# example, the default style sheet generated by doxygen has an example that
+# shows how to put an image at the root of the tree instead of the PROJECT_NAME.
+# Since the tree basically has the same information as the tab index, you could
+# consider setting DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW      = YES
+
+# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the
+# FULL_SIDEBAR option determines if the side bar is limited to only the treeview
+# area (value NO) or if it should extend to the full height of the window (value
+# YES). Setting this to YES gives a layout similar to
+# https://docs.readthedocs.io with more room for contents, but less room for the
+# project logo, title, and description. If either GENERATOR_TREEVIEW or
+# DISABLE_INDEX is set to NO, this option has no effect.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FULL_SIDEBAR           = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
-QHP_CUST_FILTER_NAME   = 
+TREEVIEW_WIDTH         = 250
+
+# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
-# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see 
-# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">Qt Help Project / Custom Filters</a>.
+EXT_LINKS_IN_WINDOW    = NO
 
-QHP_CUST_FILTER_ATTRS  = 
+# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg
+# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see
+# https://inkscape.org) to generate formulas as SVG images instead of PNGs for
+# the HTML output. These images will generally look nicer at scaled resolutions.
+# Possible values are: png (the default) and svg (looks nicer but requires the
+# pdf2svg or inkscape tool).
+# The default value is: png.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
-# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's 
-# filter section matches. 
-# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">Qt Help Project / Filter Attributes</a>.
+HTML_FORMULA_FORMAT    = svg
 
-QHP_SECT_FILTER_ATTRS  = 
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
-# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can 
-# be used to specify the location of Qt's qhelpgenerator. 
-# If non-empty doxygen will try to run qhelpgenerator on the generated 
-# .qhp file.
+FORMULA_FONTSIZE       = 10
 
-QHG_LOCATION           = 
+# 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.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT    = YES
+
+# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands
+# to create new LaTeX commands to be used in formulas as building blocks. See
+# the section "Including formulas" for details.
+
+FORMULA_MACROFILE      =
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# 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
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX            = NO
+
+# With MATHJAX_VERSION it is possible to specify the MathJax version to be used.
+# Note that the different versions of MathJax have different requirements with
+# regards to the different settings, so it is possible that also other MathJax
+# settings have to be changed when switching between the different MathJax
+# versions.
+# Possible values are: MathJax_2 and MathJax_3.
+# The default value is: MathJax_2.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_VERSION        = MathJax_2
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. For more details about the output format see MathJax
+# version 2 (see:
+# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3
+# (see:
+# http://docs.mathjax.org/en/latest/web/components/output.html).
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility. This is the name for Mathjax version 2, for MathJax version 3
+# this will be translated into chtml), NativeMML (i.e. MathML. Only supported
+# for NathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This
+# is the name for Mathjax version 3, for MathJax version 2 this will be
+# translated into HTML-CSS) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT         = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# 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 https://www.mathjax.org before deployment. The default value is:
+# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2
+# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# for MathJax version 2 (see
+# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions):
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# For example for MathJax version 3 (see
+# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html):
+# MATHJAX_EXTENSIONS = ams
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS     =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see:
+# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE       =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE           = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using JavaScript. There
+# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
+# setting. When disabled, doxygen will generate a PHP script for searching and
+# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
+# and searching needs to be provided by external tools. See the section
+# "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH    = YES
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see:
+# https://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
 
-# The DISABLE_INDEX tag can be used to turn on/off the condensed index at 
-# top of each HTML page. The value NO (the default) enables the index and 
-# the value YES disables it.
+EXTERNAL_SEARCH        = NO
 
-DISABLE_INDEX          = NO
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# 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.
 
-# This tag can be used to set the number of enum values (range [1..20]) 
-# that doxygen will group on one line in the generated HTML documentation.
+SEARCHENGINE_URL       =
 
-ENUM_VALUES_PER_LINE   = 4
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
 
-# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index 
-# structure should be generated to display hierarchical information. 
-# If the tag value is set to FRAME, a side panel will be generated 
-# containing a tree-like index structure (just like the one that 
-# is generated for HTML Help). For this to work a browser that supports 
-# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, 
-# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are 
-# probably better off using the HTML help feature. Other possible values 
-# for this tag are: HIERARCHIES, which will generate the Groups, Directories, 
-# and Class Hierarchy pages using a tree view instead of an ordered list; 
-# ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which 
-# disables this behavior completely. For backwards compatibility with previous 
-# releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE 
-# respectively.
-
-GENERATE_TREEVIEW      = NO
-
-# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be 
-# used to set the initial width (in pixels) of the frame in which the tree 
-# is shown.
+SEARCHDATA_FILE        = searchdata.xml
 
-TREEVIEW_WIDTH         = 250
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
 
-# Use this tag to change the font size of Latex formulas included 
-# as images in the HTML documentation. The default is 10. Note that 
-# when you change the font size after a successful doxygen run you need 
-# to manually remove any form_*.png images from the HTML output directory 
-# to force them to be regenerated.
+EXTERNAL_SEARCH_ID     =
 
-FORMULA_FONTSIZE       = 10
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS  =
 
 #---------------------------------------------------------------------------
-# configuration options related to the LaTeX output
+# Configuration options related to the LaTeX output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will 
-# generate Latex output.
+# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
+# The default value is: YES.
 
 GENERATE_LATEX         = NO
 
-# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. 
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
-# put in front of it. If left blank `latex' will be used as the default path.
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
 
 LATEX_OUTPUT           = latex
 
-# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be 
-# invoked. If left blank `latex' will be used as the default command name.
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# 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. If left blank `makeindex' will be used as the 
-# default command name.
+# 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
 
-# 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.
-
-COMPACT_LATEX          = NO
-
-# The PAPER_TYPE tag can be used to set the paper type that is used 
-# by the printer. Possible values are: a4, a4wide, letter, legal and 
-# executive. If left blank a4wide will be used.
+# 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.
 
-PAPER_TYPE             = a4wide
+LATEX_MAKEINDEX_CMD    = makeindex
 
-# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX 
-# packages that should be included in the LaTeX output.
+# 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.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
 
-EXTRA_PACKAGES         = 
-
-# The LATEX_HEADER tag can be used to specify a personal LaTeX header for 
-# the generated latex document. The header should contain everything until 
-# the first chapter. If it is left blank doxygen will generate a 
-# standard header. Notice: only use this tag if you know what you are doing!
-
-LATEX_HEADER           = 
+COMPACT_LATEX          = NO
 
-# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated 
-# is prepared for conversion to pdf (using ps2pdf). The pdf file will 
-# contain links (just like the HTML output) instead of page references 
-# This makes the output suitable for online browsing using a pdf viewer.
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+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. 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.
+
+EXTRA_PACKAGES         =
+
+# The LATEX_HEADER tag can be used to specify a user-defined LaTeX header for
+# the generated LaTeX document. The header should contain everything until the
+# first chapter. If it is left blank doxygen will generate a standard header. It
+# is highly recommended to start with a default header using
+# doxygen -w latex new_header.tex new_footer.tex new_stylesheet.sty
+# and then modify the file new_header.tex. See also section "Doxygen usage" for
+# information on how to generate the default header that doxygen normally uses.
+#
+# Note: Only use a user-defined header if you know what you are doing!
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. The following
+# commands have a special meaning inside the header (and footer): For a
+# description of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER           =
+
+# The LATEX_FOOTER tag can be used to specify a user-defined LaTeX footer for
+# the generated LaTeX document. The footer should contain everything after the
+# last chapter. If it is left blank doxygen will generate a standard footer. See
+# LATEX_HEADER for more information on how to generate a default footer and what
+# special commands can be used inside the footer. See also section "Doxygen
+# usage" for information on how to generate the default footer that doxygen
+# normally uses. Note: Only use a user-defined footer if you know what you are
+# doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER           =
+
+# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# LaTeX style sheets that are included after the standard style sheets created
+# by doxygen. Using this option one can overrule certain style aspects. Doxygen
+# will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_STYLESHEET =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES      =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
 
 PDF_HYPERLINKS         = YES
 
-# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of 
-# plain latex in the generated Makefile. Set this option to YES to get a 
-# higher quality PDF documentation.
+# If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as
+# specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX
+# files. Set this option to YES, to get a higher quality PDF documentation.
+#
+# See also section LATEX_CMD_NAME for selecting the engine.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
 
 USE_PDFLATEX           = YES
 
-# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. 
-# command to the generated LaTeX files. This will instruct LaTeX to keep 
-# running if errors occur, instead of asking the user for help. 
-# This option is also used when generating formulas in HTML.
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
 
 LATEX_BATCHMODE        = NO
 
-# If LATEX_HIDE_INDICES is set to YES then doxygen will not 
-# include the index chapters (such as File Index, Compound Index, etc.) 
-# in the output.
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
 
 LATEX_HIDE_INDICES     = NO
 
-# If LATEX_SOURCE_CODE is set to YES then doxygen will include
-# source code with syntax highlighting in the LaTeX output.
-# Note that which sources are shown also depends on other settings
-# such as SOURCE_BROWSER.
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# 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_SOURCE_CODE      = NO
+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
+# Configuration options related to the RTF output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output 
-# The RTF output is optimized for Word 97 and may not look very pretty with 
-# other RTF readers or editors.
+# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
 
 GENERATE_RTF           = NO
 
-# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. 
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
-# put in front of it. If left blank `rtf' will be used as the default path.
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
 
 RTF_OUTPUT             = rtf
 
-# If the COMPACT_RTF tag is set to YES Doxygen generates more compact 
-# RTF documents. This may be useful for small projects and may help to 
-# save some trees in general.
+# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
 
 COMPACT_RTF            = NO
 
-# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated 
-# will contain hyperlink fields. The RTF file will 
-# contain links (just like the HTML output) instead of page references. 
-# This makes the output suitable for online browsing using WORD or other 
-# programs which support those fields. 
-# Note: wordpad (write) and others do not support links.
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
 
 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 
+# 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.
+# This tag requires that the tag GENERATE_RTF is set to YES.
 
-RTF_STYLESHEET_FILE    = 
+RTF_STYLESHEET_FILE    =
 
-# Set optional variables used in the generation of an rtf document. 
-# Syntax is similar to doxygen's config file.
+# Set optional variables used in the generation of an RTF document. Syntax is
+# 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    = 
+RTF_EXTENSIONS_FILE    =
 
 #---------------------------------------------------------------------------
-# configuration options related to the man page output
+# Configuration options related to the man page output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_MAN tag is set to YES (the default) Doxygen will 
-# generate man pages
+# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
 
 GENERATE_MAN           = NO
 
-# The MAN_OUTPUT tag is used to specify where the man pages will be put. 
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
-# put in front of it. If left blank `man' will be used as the default path.
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
 
 MAN_OUTPUT             = man
 
-# The MAN_EXTENSION tag determines the extension that is added to 
-# the generated man pages (default is the subroutine's section .3)
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
 
 MAN_EXTENSION          = .3
 
-# If the MAN_LINKS tag is set to YES and Doxygen generates man output, 
-# then it will generate one additional man file for each entity 
-# documented in the real man page(s). These additional files 
-# only source the real man page, but without them the man command 
-# would be unable to find the correct page. The default is NO.
+# The MAN_SUBDIR tag determines the name of the directory created within
+# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
+# MAN_EXTENSION with the initial . removed.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_SUBDIR             =
+
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
 
 MAN_LINKS              = NO
 
 #---------------------------------------------------------------------------
-# configuration options related to the XML output
+# Configuration options related to the XML output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_XML tag is set to YES Doxygen will 
-# generate an XML file that captures the structure of 
-# the code including all documentation.
+# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
 
 GENERATE_XML           = NO
 
-# The XML_OUTPUT tag is used to specify where the XML pages will be put. 
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
-# put in front of it. If left blank `xml' will be used as the default path.
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
 
 XML_OUTPUT             = xml
 
-# The XML_SCHEMA tag can be used to specify an XML schema, 
-# which can be used by a validating XML parser to check the 
-# syntax of the XML files.
+# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
 
-XML_SCHEMA             = 
+XML_PROGRAMLISTING     = YES
 
-# The XML_DTD tag can be used to specify an XML DTD, 
-# which can be used by a validating XML parser to check the 
-# syntax of the XML files.
+# 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_DTD                = 
+XML_NS_MEMB_FILE_SCOPE = NO
 
-# If the XML_PROGRAMLISTING tag is set to YES Doxygen will 
-# dump the program listings (including syntax highlighting 
-# and cross-referencing information) to the XML output. Note that 
-# enabling this will significantly increase the size of the XML output.
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
 
-XML_PROGRAMLISTING     = YES
+# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK       = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT         = docbook
 
 #---------------------------------------------------------------------------
-# configuration options for the AutoGen Definitions output
+# Configuration options for the AutoGen Definitions output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will 
-# generate an AutoGen Definitions (see 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.
+# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
+# 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
 
 #---------------------------------------------------------------------------
-# configuration options related to the Perl module output
+# Configuration options related to Sqlite3 output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_PERLMOD tag is set to YES Doxygen will 
-# generate a Perl module file that captures the structure of 
-# the code including all documentation. Note that this 
-# feature is still experimental and incomplete at the 
-# moment.
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module
+# 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_PERLMOD       = NO
 
-# If the PERLMOD_LATEX tag is set to YES Doxygen will generate 
-# the necessary Makefile rules, Perl scripts and LaTeX code to be able 
-# to generate PDF and DVI output from the Perl module output.
+# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
 
 PERLMOD_LATEX          = NO
 
-# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be 
-# nicely formatted so it can be parsed by a human reader.  This is useful 
-# if you want to understand what is going on.  On the other hand, if this 
-# tag is set to NO the size of the Perl module output will be much smaller 
-# and Perl will parse it just the same.
+# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO, the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
 
 PERLMOD_PRETTY         = YES
 
-# The names of the make variables in the generated doxyrules.make file 
-# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. 
-# This is useful so different doxyrules.make files included by the same 
-# Makefile don't overwrite each other's variables.
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
 
-PERLMOD_MAKEVAR_PREFIX = 
+PERLMOD_MAKEVAR_PREFIX =
 
 #---------------------------------------------------------------------------
-# Configuration options related to the preprocessor   
+# Configuration options related to the preprocessor
 #---------------------------------------------------------------------------
 
-# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will 
-# evaluate all C-preprocessor directives found in the sources and include 
-# files.
+# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
 
 ENABLE_PREPROCESSING   = YES
 
-# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro 
-# names in the source code. If set to NO (the default) only conditional 
-# compilation will be performed. Macro expansion can be done in a controlled 
-# way by setting EXPAND_ONLY_PREDEF to YES.
+# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names
+# in the source code. If set to NO, only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
 
-MACRO_EXPANSION        = NO
+MACRO_EXPANSION        = YES
 
-# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES 
-# then the macro expansion is limited to the macros specified with the 
-# PREDEFINED and EXPAND_AS_DEFINED tags.
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
 
-EXPAND_ONLY_PREDEF     = NO
+EXPAND_ONLY_PREDEF     = YES
 
-# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files 
-# in the INCLUDE_PATH (see below) will be search if a #include is found.
+# If the SEARCH_INCLUDES tag is set to YES, the include files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
 
 SEARCH_INCLUDES        = YES
 
-# The INCLUDE_PATH tag can be used to specify one or more directories that 
-# contain include files that are not input files but should be processed by 
-# the preprocessor.
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
 
-INCLUDE_PATH           = 
+INCLUDE_PATH           =
 
-# 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 
-# directories. If left blank, the patterns specified with FILE_PATTERNS will 
-# be used.
+# 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
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
 
-INCLUDE_FILE_PATTERNS  = 
+INCLUDE_FILE_PATTERNS  =
 
-# The PREDEFINED tag can be used to specify one or more macro names that 
-# are defined before the preprocessor is started (similar to the -D option of 
-# gcc). The argument of the tag is a list of macros of the form: name 
-# or name=definition (no spaces). If the definition and the = are 
-# omitted =1 is assumed. To prevent a macro definition from being 
-# undefined via #undef or recursively expanded use the := operator 
-# instead of the = operator.
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
 
-PREDEFINED             = 
+PREDEFINED             =
 
-# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then 
-# this tag can be used to specify a list of macro names that should be expanded. 
-# The macro definition that is found in the sources will be used. 
-# Use the PREDEFINED tag if you want to use a different macro definition.
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
 
-EXPAND_AS_DEFINED      = 
+EXPAND_AS_DEFINED      =
 
-# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then 
-# doxygen's preprocessor will remove all function-like macros that are alone 
-# on a line, have an all uppercase name, and do not end with a semicolon. Such 
-# function macros are typically used for boiler-plate code, and will confuse 
-# the parser if not removed.
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all references to function-like macros that are alone on a line, have
+# an all uppercase name, and do not end with a semicolon. Such function macros
+# are typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
 
 SKIP_FUNCTION_MACROS   = YES
 
 #---------------------------------------------------------------------------
-# Configuration::additions related to external references   
+# Configuration options related to external references
 #---------------------------------------------------------------------------
 
-# The TAGFILES option can be used to specify one or more tagfiles. 
-# Optionally an initial location of the external documentation 
-# can be added for each tagfile. The format of a tag file without 
-# this location is as follows: 
-#   TAGFILES = file1 file2 ... 
-# Adding location for the tag files is done as follows: 
-#   TAGFILES = file1=loc1 "file2 = loc2" ... 
-# where "loc1" and "loc2" can be relative or absolute paths or 
-# URLs. If a location is present for each tag, the installdox tool 
-# does not have to be run to correct the links. 
-# Note that each tag file must have a unique name 
-# (where the name does NOT include the path) 
-# If a tag file is not located in the directory in which doxygen 
-# is run, you must also specify the path to the tagfile here.
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have a unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
 
-TAGFILES               = 
+TAGFILES               =
 
-# When a file name is specified after GENERATE_TAGFILE, doxygen will create 
-# a tag file that is based on the input files it reads.
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
 
-GENERATE_TAGFILE       = 
+GENERATE_TAGFILE       =
 
-# If the ALLEXTERNALS tag is set to YES all external classes will be listed 
-# in the class index. If set to NO only the inherited external classes 
-# will be listed.
+# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
+# the class index. If set to NO, only the inherited external classes will be
+# listed.
+# The default value is: NO.
 
 ALLEXTERNALS           = NO
 
-# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed 
-# in the modules index. If set to NO, only the current project's groups will 
-# be listed.
+# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
 
 EXTERNAL_GROUPS        = YES
 
-# The PERL_PATH should be the absolute path and name of the perl script 
-# interpreter (i.e. the result of `which perl').
+# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
 
-PERL_PATH              = /usr/bin/perl
+EXTERNAL_PAGES         = YES
 
 #---------------------------------------------------------------------------
-# Configuration options related to the dot tool   
+# Configuration options related to the dot tool
 #---------------------------------------------------------------------------
 
-# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will 
-# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base 
-# or super classes. Setting the tag to NO turns the diagrams off. Note that 
-# this option is superseded by the HAVE_DOT option below. This is only a 
-# fallback. It is recommended to install and use dot, since it yields more 
+# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
 # powerful graphs.
+# The default value is: YES.
 
 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.
+# 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.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH               =
+
+# If set to YES the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
 
-MSCGEN_PATH            = 
+HIDE_UNDOC_RELATIONS   = NO
 
-# If set to YES, the inheritance and collaboration graphs will hide 
-# inheritance and usage relations if the target is undocumented 
-# or is not a class.
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: NO.
 
-HIDE_UNDOC_RELATIONS   = YES
+HAVE_DOT               = YES
 
-# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is 
-# available from the path. This tool is part of Graphviz, a graph visualization 
-# toolkit from AT&T and Lucent Bell Labs. The other options in this section 
-# have no effect if this option is set to NO (the default)
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
-HAVE_DOT               = NO
+DOT_NUM_THREADS        = 0
 
-# By default doxygen will write a font called FreeSans.ttf to the output 
-# directory and reference it in all dot files that doxygen generates. This 
-# font does not include all possible unicode characters however, so when you need 
-# these (or just want a differently looking font) you can specify the font name 
-# using DOT_FONTNAME. You need need to make sure dot is able to find the font, 
-# which can be done by putting it in a standard location or by setting the 
-# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory 
-# containing the font.
+# When you want a differently looking font in the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
-DOT_FONTNAME           = FreeSans
+DOT_FONTNAME           = Helvetica
 
-# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. 
-# The default size is 10pt.
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 DOT_FONTSIZE           = 10
 
-# By default doxygen will tell dot to use the output directory to look for the 
-# FreeSans.ttf font (which doxygen will put there itself). If you specify a 
-# different font using DOT_FONTNAME you can set the path where dot 
-# can find it using this tag.
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
-DOT_FONTPATH           = 
+DOT_FONTPATH           =
 
-# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen 
-# will generate a graph for each documented class showing the direct and 
-# indirect inheritance relations. Setting this tag to YES will force the 
-# the CLASS_DIAGRAMS tag to NO.
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 CLASS_GRAPH            = YES
 
-# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen 
-# will generate a graph for each documented class showing the direct and 
-# indirect implementation dependencies (inheritance, containment, and 
-# class references variables) of the class with other documented classes.
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 COLLABORATION_GRAPH    = YES
 
-# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen 
-# will generate a graph for groups, showing the direct groups dependencies
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 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 
+# 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
 # Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 UML_LOOK               = NO
 
-# If set to YES, the inheritance and collaboration graphs will show the 
-# relations between templates and their instances.
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag UML_LOOK is set to YES.
+
+UML_LIMIT_NUM_FIELDS   = 10
+
+# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and
+# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS
+# tag is set to YES, doxygen will add type and arguments for attributes and
+# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen
+# will not generate fields with class member information in the UML graphs. The
+# class diagrams will look similar to the default class diagrams but using UML
+# notation for the relationships.
+# Possible values are: NO, YES and NONE.
+# The default value is: NO.
+# This tag requires that the tag UML_LOOK is set to YES.
+
+DOT_UML_DETAILS        = NO
+
+# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters
+# to display on a single line. If the actual line length exceeds this threshold
+# significantly it will wrapped across multiple lines. Some heuristics are apply
+# to avoid ugly line breaks.
+# Minimum value: 0, maximum value: 1000, default value: 17.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_WRAP_THRESHOLD     = 17
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 TEMPLATE_RELATIONS     = NO
 
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT 
-# tags are set to YES then doxygen will generate a graph for each documented 
-# file showing the direct and indirect include dependencies of the file with 
-# other documented files.
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 INCLUDE_GRAPH          = YES
 
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and 
-# HAVE_DOT tags are set to YES then doxygen will generate a graph for each 
-# documented header file showing the documented files that directly or 
-# indirectly include this file.
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 INCLUDED_BY_GRAPH      = YES
 
-# If the CALL_GRAPH and HAVE_DOT options are set to YES then 
-# doxygen will generate a call dependency graph for every global function 
-# or class method. 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.
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# 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. 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.
 
 CALL_GRAPH             = NO
 
-# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then 
-# doxygen will generate a caller dependency graph for every global function 
-# or class method. 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.
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# 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. 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.
 
 CALLER_GRAPH           = NO
 
-# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen 
-# will graphical hierarchy of all classes instead of a textual one.
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 GRAPHICAL_HIERARCHY    = YES
 
-# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES 
-# then doxygen will show the dependencies a directory has on other directories 
-# in a graphical way. The dependency relations are determined by the #include 
-# relations between the files in the directories.
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 DIRECTORY_GRAPH        = YES
 
-# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images 
-# generated by dot. Possible values are png, jpg, or gif 
-# If left blank png will be used.
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# 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, jpg, gif, 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.
+
+DOT_IMAGE_FORMAT       = svg
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
-DOT_IMAGE_FORMAT       = png
+INTERACTIVE_SVG        = YES
 
-# The tag DOT_PATH can be used to specify the path where the dot tool can be 
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
 # found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_PATH               =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOTFILE_DIRS           =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS           =
+
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS           =
+
+# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
+# path where java can find the plantuml.jar file. If left blank, it is assumed
+# PlantUML is not used or called during a preprocessing step. Doxygen will
+# generate a warning when it encounters a \startuml command in this case and
+# will not generate output for the diagram.
+
+PLANTUML_JAR_PATH      =
 
-DOT_PATH               = 
+# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a
+# configuration file for plantuml.
 
-# The DOTFILE_DIRS tag can be used to specify one or more directories that 
-# contain dot files that are included in the documentation (see the 
-# \dotfile command).
+PLANTUML_CFG_FILE      =
 
-DOTFILE_DIRS           = 
+# When using plantuml, the specified paths are searched for files specified by
+# the !include statement in a plantuml block.
 
-# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of 
-# nodes that will be shown in the graph. If the number of nodes in a graph 
-# becomes larger than this value, doxygen will truncate the graph, which is 
-# visualized by representing a node as a red box. Note that doxygen if the 
-# number of direct children of the root node in a graph is already larger than 
-# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note 
-# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+PLANTUML_INCLUDE_PATH  =
 
-DOT_GRAPH_MAX_NODES    = 50
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
-# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the 
-# graphs generated by dot. A depth value of 3 means that only nodes reachable 
-# from the root by following a path via at most 3 edges will be shown. Nodes 
-# that lay further from the root node will be omitted. Note that setting this 
-# option to 1 or 2 may greatly reduce the computation time needed for large 
-# code bases. Also note that the size of a graph can be further restricted by 
+DOT_GRAPH_MAX_NODES    = 500
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
 # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 MAX_DOT_GRAPH_DEPTH    = 0
 
-# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent 
-# background. This is disabled by default, because dot on Windows does not 
-# seem to support this out of the box. Warning: Depending on the platform used, 
-# enabling this option may lead to badly anti-aliased labels on the edges of 
-# a graph (i.e. they become hard to read).
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
-DOT_TRANSPARENT        = NO
+DOT_TRANSPARENT        = YES
 
-# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output 
-# files in one run (i.e. multiple -o and -T options on the command line). This 
-# makes dot run faster, but since only newer versions of dot (>1.8.10) 
-# support this, this feature is disabled by default.
+# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
-DOT_MULTI_TARGETS      = NO
+DOT_MULTI_TARGETS      = YES
 
-# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will 
-# generate a legend page explaining the meaning of the various boxes and 
-# arrows in the dot generated graphs.
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 GENERATE_LEGEND        = YES
 
-# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will 
-# remove the intermediate dot files that are used to generate 
-# the various graphs.
+# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate
+# files that are used to generate the various graphs.
+#
+# Note: This setting is not only used for dot files but also for msc temporary
+# files.
+# The default value is: YES.
 
 DOT_CLEANUP            = YES
-
-#---------------------------------------------------------------------------
-# Options related to the search engine
-#---------------------------------------------------------------------------
-
-# The SEARCHENGINE tag specifies whether or not a search engine should be 
-# used. If set to NO the values of all tags below this one will be ignored.
-
-SEARCHENGINE           = NO
index da83a1234a9ab677554f3dc162ecb778c8afca31..df037d5c395d915012a76c835b768496c7d7751f 100644 (file)
--- a/README.md
+++ b/README.md
@@ -5,26 +5,64 @@ improves and builds upon the original 1996 engine by adding modern rendering
 features, and expanding upon the engine's native game code language QuakeC, as
 well as supporting additional map and model formats.
 
-Developed by LadyHavoc. See CREDITS.md for a list of contributors.
+Developed by LadyHavoc. See [CREDITS](CREDITS.md) for a list of contributors.
 
 ## Help/support
 
-### IRC:
+### IRC
 #darkplaces on irc.anynet.org
 
-### Discord:
-https://discord.gg/ZHT9QeW
+### [Matrix](https://matrix.org/docs/guides/introduction)
+[![#darkplaces:matrix.org](https://img.shields.io/matrix/darkplaces:matrix.org?color=660000&label=%23darkplaces%3Amatrix.org)](https://matrix.to/#/#darkplaces:matrix.org)
+
+## Downloading and running
+
+Linux x86_64 builds are available in [GitHub CI](https://github.com/DarkPlacesEngine/darkplaces/actions?query=branch%3Amaster) artifacts.  
+
+More complete builds are available in [xonotic.org](https://beta.xonotic.org/autobuild/) engine zips.  
+These support Windows, Linux and macOS, and include the current libraries needed for all features.
+
+DarkPlaces supports many Quake-based games and you can select which it will run by renaming the executable so it's prefixed with the game's name, for example `rogue-sdl.exe`, or by passing a cmdline argument  
+such as `-rogue`.  This changes various engine behaviours and cvar defaults to suit the game.  
+The supported list and related details are defined in [com_game.c](https://github.com/DarkPlacesEngine/darkplaces/blob/master/com_game.c).
+
+Mods which aren't listed there can be run with (for example) `-game quake15` in which case DP will use the same behaviours and cvar defaults as for id1 Quake.
+
+## Quake Virtual File System
+
+All of Quake's data access is through a hierarchical file system, the contents
+of the file system can be transparently merged from several sources.
+
+The "base directory" is the path to the directory holding the quake.exe and
+all game directories.  This can be overridden with the "-basedir" command
+line parm to allow code debugging in a different directory.  The base
+directory is only used during filesystem initialization.
+
+The "game directory" is the first tree on the search path and directory that
+all generated files (savegames, screenshots, demos, config files) will be
+saved to.  This can be overridden with the "-game" command line parameter.
+If multiple "-game <gamedir>" args are passed the last one is the "primary"
+and files will be saved there, the rest are read-only.
 
 ## Build instructions (WIP)
 
-You will need the following packages regardless of platform:
-* SDL2
-* libjpeg
-* libpng
-* libvorbis
-* libogg
+These instructions are adequate for Quake, but for Xonotic please refer to [its wiki](https://gitlab.com/xonotic/xonotic/-/wikis/Compiling).
+
+### Required packages
+
+The minimum SDL version is 2.0.18 for Linux and 2.24.0 for Windows.  
+The supported compilers are GCC and Clang.  
+The following package names are for Debian, see below for Windows and Mac.
+
+##### Client
+Build (mandatory): `build-essential` `libjpeg-dev` `libsdl2-dev`  
+Runtime (optional): `libcurl` `libpng` `libfreetype6` `libvorbisfile`  
 
-### Windows (MSYS2):
+##### Dedicated Server
+Build (mandatory): `build-essential` `libjpeg-dev` `zlib1g-dev`  
+Runtime (optional): `libcurl` `libpng`  
+
+### Windows (MSYS2 MinGW):
 
 1. Install MSYS2, found [here](https://www.msys2.org/).
 2. Once you've installed MSYS2 and have fully updated it, open a MinGW64 terminal (***not an MSYS2 terminal***) and input the following command:
@@ -35,10 +73,46 @@ pacman -S --needed gcc make mingw-w64-x86_64-{toolchain,libjpeg-turbo,libpng,lib
 
 3. See [Unix instructions](#unix-(general)).
 
+### macOS
+1. Open a terminal and input `xcode-select --install`
+2. Install [Homebrew](https://brew.sh)
+3. In the same (or a different terminal), input the following command:
+
+```
+brew install sdl2 libjpeg-turbo libpng libvorbis curl
+```
+
+4. See [Unix instructions](#unix-(general)).
+
 ### Unix (General)
 
-In the engine's root directory, run `make`. See `make help` for options.
+From a terminal, in the engine's root directory, input `make help` to list the targets.  
+To build the main executable, input `make sdl-release` which creates the file called  
+`darkplaces-sdl` or `darkplaces-sdl.exe` (Windows).
+
+If you get errors (that don't seem to be about missing dependencies) try `make clean` before compiling, especially if you updated your system since the last time you compiled.
+
+
+### Windows (Visual Studio 2019)
+
+Not recommended due to poor support for C standards, and lack of maintenance.
+
+DarkPlaces requires C11, so Windows SDK 10.0.20348.0 or later is needed.  
+To install it, run the Visual Studio Installer, click "Modify", click "Individual components", type "Windows SDK" in the search box, select the latest Windows SDK and de-select older versions.  
+You will also need "NuGet package manager" selected (to download SDL2 headers the first time you build).
+Click "Modify" to apply the changes.  
+
+Open `darkplaces-vs2019.sln`, select build type (`Debug` or `Release`) and platform (`Win32` or `x64`), and choose "Build Solution" from the "Build" menu to create files `darkplaces-sdl2-vs2019.exe` and `SDL2.dll`.
+
+The Release build crashes. The Debug x64 build doesn't crash (but is rather slow) so this will be Fun for someone to debug.
+
+To get a build suitable for playing you'll need to use MinGW GCC, or download the autobuild from Xonotic (see above).
+
+
+## Contributing
+
+[DarkPlaces Contributing Guidelines](CONTRIBUTING.md)
 
-### Windows (Visual Studio)
+## Documentation
 
-Instructions coming soon.
+Doxygen: https://xonotic.org/doxygen/darkplaces
diff --git a/SDLMain.h b/SDLMain.h
deleted file mode 100644 (file)
index 4683df5..0000000
--- a/SDLMain.h
+++ /dev/null
@@ -1,11 +0,0 @@
-/*   SDLMain.m - main entry point for our Cocoa-ized SDL app
-       Initial Version: Darrell Walisser <dwaliss1@purdue.edu>
-       Non-NIB-Code & other changes: Max Horn <max@quendi.de>
-
-    Feel free to customize this file to suit your needs
-*/
-
-#import <Cocoa/Cocoa.h>
-
-@interface SDLMain : NSObject
-@end
diff --git a/SDLMain.m b/SDLMain.m
deleted file mode 100644 (file)
index 6eadb9e..0000000
--- a/SDLMain.m
+++ /dev/null
@@ -1,385 +0,0 @@
-/*   SDLMain.m - main entry point for our Cocoa-ized SDL app
-       Initial Version: Darrell Walisser <dwaliss1@purdue.edu>
-       Non-NIB-Code & other changes: Max Horn <max@quendi.de>
-
-    Feel free to customize this file to suit your needs
-*/
-
-#include <SDL.h>
-
-#if SDL_MAJOR_VERSION == 1
-
-#include "SDLMain.h"
-#include <sys/param.h> /* for MAXPATHLEN */
-#include <unistd.h>
-
-/* For some reaon, Apple removed setAppleMenu from the headers in 10.4,
- but the method still is there and works. To avoid warnings, we declare
- it ourselves here. */
-@interface NSApplication(SDL_Missing_Methods)
-- (void)setAppleMenu:(NSMenu *)menu;
-@end
-
-/* Use this flag to determine whether we use SDLMain.nib or not */
-#define                SDL_USE_NIB_FILE        0
-
-/* Use this flag to determine whether we use CPS (docking) or not */
-#define                SDL_USE_CPS             1
-#ifdef SDL_USE_CPS
-/* Portions of CPS.h */
-typedef struct CPSProcessSerNum
-{
-       UInt32          lo;
-       UInt32          hi;
-} CPSProcessSerNum;
-
-extern OSErr   CPSGetCurrentProcess( CPSProcessSerNum *psn);
-extern OSErr   CPSEnableForegroundOperation( CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5);
-extern OSErr   CPSSetFrontProcess( CPSProcessSerNum *psn);
-
-#endif /* SDL_USE_CPS */
-
-static int    gArgc;
-static char  **gArgv;
-static BOOL   gFinderLaunch;
-static BOOL   gCalledAppMainline = FALSE;
-
-static NSString *getApplicationName(void)
-{
-    const NSDictionary *dict;
-    NSString *appName = 0;
-
-    /* Determine the application name */
-    dict = (const NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetMainBundle());
-    if (dict)
-        appName = [dict objectForKey: @"CFBundleName"];
-    
-    if (![appName length])
-        appName = [[NSProcessInfo processInfo] processName];
-
-    return appName;
-}
-
-#if SDL_USE_NIB_FILE
-/* A helper category for NSString */
-@interface NSString (ReplaceSubString)
-- (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString;
-@end
-#endif
-
-@interface NSApplication (SDLApplication)
-@end
-
-@implementation NSApplication (SDLApplication)
-/* Invoked from the Quit menu item */
-- (void)terminate:(id)sender
-{
-    /* Post a SDL_QUIT event */
-    SDL_Event event;
-    event.type = SDL_QUIT;
-    SDL_PushEvent(&event);
-}
-@end
-
-/* The main class of the application, the application's delegate */
-@implementation SDLMain
-
-/* Set the working directory to the .app's parent directory */
-- (void) setupWorkingDirectory:(BOOL)shouldChdir
-{
-    if (shouldChdir)
-    {
-        char parentdir[MAXPATHLEN];
-        CFURLRef url = CFBundleCopyBundleURL(CFBundleGetMainBundle());
-        CFURLRef url2 = CFURLCreateCopyDeletingLastPathComponent(0, url);
-        if (CFURLGetFileSystemRepresentation(url2, 1, (UInt8 *)parentdir, MAXPATHLEN)) {
-            chdir(parentdir);   /* chdir to the binary app's parent */
-        }
-        CFRelease(url);
-        CFRelease(url2);
-    }
-}
-
-#if SDL_USE_NIB_FILE
-
-/* Fix menu to contain the real app name instead of "SDL App" */
-- (void)fixMenu:(NSMenu *)aMenu withAppName:(NSString *)appName
-{
-    NSRange aRange;
-    NSEnumerator *enumerator;
-    NSMenuItem *menuItem;
-
-    aRange = [[aMenu title] rangeOfString:@"SDL App"];
-    if (aRange.length != 0)
-        [aMenu setTitle: [[aMenu title] stringByReplacingRange:aRange with:appName]];
-
-    enumerator = [[aMenu itemArray] objectEnumerator];
-    while ((menuItem = [enumerator nextObject]))
-    {
-        aRange = [[menuItem title] rangeOfString:@"SDL App"];
-        if (aRange.length != 0)
-            [menuItem setTitle: [[menuItem title] stringByReplacingRange:aRange with:appName]];
-        if ([menuItem hasSubmenu])
-            [self fixMenu:[menuItem submenu] withAppName:appName];
-    }
-}
-
-#else
-
-static void setApplicationMenu(void)
-{
-    /* warning: this code is very odd */
-    NSMenu *appleMenu;
-    NSMenuItem *menuItem;
-    NSString *title;
-    NSString *appName;
-    
-    appName = getApplicationName();
-    appleMenu = [[NSMenu alloc] initWithTitle:@""];
-    
-    /* Add menu items */
-    title = [@"About " stringByAppendingString:appName];
-    [appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
-
-    [appleMenu addItem:[NSMenuItem separatorItem]];
-
-    title = [@"Hide " stringByAppendingString:appName];
-    [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"];
-
-    menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
-    [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];
-
-    [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
-
-    [appleMenu addItem:[NSMenuItem separatorItem]];
-
-    title = [@"Quit " stringByAppendingString:appName];
-    [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"];
-
-    
-    /* Put menu into the menubar */
-    menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
-    [menuItem setSubmenu:appleMenu];
-    [[NSApp mainMenu] addItem:menuItem];
-
-    /* Tell the application object that this is now the application menu */
-    [NSApp setAppleMenu:appleMenu];
-
-    /* Finally give up our references to the objects */
-    [appleMenu release];
-    [menuItem release];
-}
-
-/* Create a window menu */
-static void setupWindowMenu(void)
-{
-    NSMenu      *windowMenu;
-    NSMenuItem  *windowMenuItem;
-    NSMenuItem  *menuItem;
-
-    windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
-    
-    /* "Minimize" item */
-    menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"];
-    [windowMenu addItem:menuItem];
-    [menuItem release];
-    
-    /* Put menu into the menubar */
-    windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""];
-    [windowMenuItem setSubmenu:windowMenu];
-    [[NSApp mainMenu] addItem:windowMenuItem];
-    
-    /* Tell the application object that this is now the window menu */
-    [NSApp setWindowsMenu:windowMenu];
-
-    /* Finally give up our references to the objects */
-    [windowMenu release];
-    [windowMenuItem release];
-}
-
-/* Replacement for NSApplicationMain */
-static void CustomApplicationMain (int argc, char **argv)
-{
-    NSAutoreleasePool  *pool = [[NSAutoreleasePool alloc] init];
-    SDLMain                            *sdlMain;
-
-    /* Ensure the application object is initialised */
-    [NSApplication sharedApplication];
-    
-#ifdef SDL_USE_CPS
-    {
-        CPSProcessSerNum PSN;
-        /* Tell the dock about us */
-        if (!CPSGetCurrentProcess(&PSN))
-            if (!CPSEnableForegroundOperation(&PSN,0x03,0x3C,0x2C,0x1103))
-                if (!CPSSetFrontProcess(&PSN))
-                    [NSApplication sharedApplication];
-    }
-#endif /* SDL_USE_CPS */
-
-    /* Set up the menubar */
-    [NSApp setMainMenu:[[NSMenu alloc] init]];
-    setApplicationMenu();
-    setupWindowMenu();
-
-    /* Create SDLMain and make it the app delegate */
-    sdlMain = [[SDLMain alloc] init];
-    [NSApp setDelegate:sdlMain];
-    
-    /* Start the main event loop */
-    [NSApp run];
-    
-    [sdlMain release];
-    [pool release];
-}
-
-#endif
-
-
-/*
- * Catch document open requests...this lets us notice files when the app
- *  was launched by double-clicking a document, or when a document was
- *  dragged/dropped on the app's icon. You need to have a
- *  CFBundleDocumentsType section in your Info.plist to get this message,
- *  apparently.
- *
- * Files are added to gArgv, so to the app, they'll look like command line
- *  arguments. Previously, apps launched from the finder had nothing but
- *  an argv[0].
- *
- * This message may be received multiple times to open several docs on launch.
- *
- * This message is ignored once the app's mainline has been called.
- */
-- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
-{
-    const char *temparg;
-    size_t arglen;
-    char *arg;
-    char **newargv;
-
-    if (!gFinderLaunch)  /* MacOS is passing command line args. */
-        return FALSE;
-
-    if (gCalledAppMainline)  /* app has started, ignore this document. */
-        return FALSE;
-
-    temparg = [filename UTF8String];
-    arglen = SDL_strlen(temparg) + 1;
-    arg = (char *) SDL_malloc(arglen);
-    if (arg == NULL)
-        return FALSE;
-
-    newargv = (char **) realloc(gArgv, sizeof (char *) * (gArgc + 2));
-    if (newargv == NULL)
-    {
-        SDL_free(arg);
-        return FALSE;
-    }
-    gArgv = newargv;
-
-    SDL_strlcpy(arg, temparg, arglen);
-    gArgv[gArgc++] = arg;
-    gArgv[gArgc] = NULL;
-    return TRUE;
-}
-
-
-/* Called when the internal event loop has just started running */
-- (void) applicationDidFinishLaunching: (NSNotification *) note
-{
-    int status;
-
-    /* Set the working directory to the .app's parent directory */
-    [self setupWorkingDirectory:gFinderLaunch];
-
-#if SDL_USE_NIB_FILE
-    /* Set the main menu to contain the real app name instead of "SDL App" */
-    [self fixMenu:[NSApp mainMenu] withAppName:getApplicationName()];
-#endif
-
-    /* Hand off to main application code */
-    gCalledAppMainline = TRUE;
-    status = SDL_main (gArgc, gArgv);
-
-    /* We're done, thank you for playing */
-    exit(status);
-}
-@end
-
-
-@implementation NSString (ReplaceSubString)
-
-- (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString
-{
-    unsigned int bufferSize;
-    unsigned int selfLen = [self length];
-    unsigned int aStringLen = [aString length];
-    unichar *buffer;
-    NSRange localRange;
-    NSString *result;
-
-    bufferSize = selfLen + aStringLen - aRange.length;
-    buffer = (unichar *)NSAllocateMemoryPages(bufferSize*sizeof(unichar));
-    
-    /* Get first part into buffer */
-    localRange.location = 0;
-    localRange.length = aRange.location;
-    [self getCharacters:buffer range:localRange];
-    
-    /* Get middle part into buffer */
-    localRange.location = 0;
-    localRange.length = aStringLen;
-    [aString getCharacters:(buffer+aRange.location) range:localRange];
-     
-    /* Get last part into buffer */
-    localRange.location = aRange.location + aRange.length;
-    localRange.length = selfLen - localRange.location;
-    [self getCharacters:(buffer+aRange.location+aStringLen) range:localRange];
-    
-    /* Build output string */
-    result = [NSString stringWithCharacters:buffer length:bufferSize];
-    
-    NSDeallocateMemoryPages(buffer, bufferSize);
-    
-    return result;
-}
-
-@end
-
-
-
-#ifdef main
-#  undef main
-#endif
-
-
-/* Main entry point to executable - should *not* be SDL_main! */
-int main (int argc, char **argv)
-{
-    /* Copy the arguments into a global variable */
-    /* This is passed if we are launched by double-clicking */
-    if ( argc >= 2 && strncmp (argv[1], "-psn", 4) == 0 ) {
-        gArgv = (char **) SDL_malloc(sizeof (char *) * 2);
-        gArgv[0] = argv[0];
-        gArgv[1] = NULL;
-        gArgc = 1;
-        gFinderLaunch = YES;
-    } else {
-        int i;
-        gArgc = argc;
-        gArgv = (char **) SDL_malloc(sizeof (char *) * (argc+1));
-        for (i = 0; i <= argc; i++)
-            gArgv[i] = argv[i];
-        gFinderLaunch = NO;
-    }
-
-#if SDL_USE_NIB_FILE
-    NSApplicationMain (argc, argv);
-#else
-    CustomApplicationMain (argc, argv);
-#endif
-    return 0;
-}
-
-#endif
index e38d9b18fc8655a3f6eb129290838aec07a39a86..7106dc9b669e41a19a90e92c539e150da6f6f1cd 100644 (file)
@@ -3,14 +3,15 @@
 
 extern const char *buildstring;
 const char *buildstring =
-#ifndef NO_BUILD_TIMESTAMPS
-__TIME__ " " __DATE__ " "
-#endif
-#ifdef SVNREVISION
-STRINGIFY(SVNREVISION)
+#ifdef VCREVISION
+STRINGIFY(VCREVISION)
 #else
 "-"
 #endif
+#ifndef NO_BUILD_TIMESTAMPS
+//" " __TIME__
+" " __DATE__
+#endif
 #ifdef BUILDTYPE
 " " STRINGIFY(BUILDTYPE)
 #endif
index 6218e8ad5fd71d51040d5a90ace806fa4d46becc..e0f85994c661605f6791bde5e1f43ccf2c5c0ea7 100644 (file)
--- a/cap_ogg.c
+++ b/cap_ogg.c
@@ -586,6 +586,7 @@ void SCR_CaptureVideo_Ogg_Init(void)
        Cvar_RegisterVariable(&cl_capturevideo_ogg_theora_keyframe_mininterval);
        Cvar_RegisterVariable(&cl_capturevideo_ogg_theora_keyframe_auto_threshold);
        Cvar_RegisterVariable(&cl_capturevideo_ogg_theora_noise_sensitivity);
+       Cvar_RegisterVariable(&cl_capturevideo_ogg_theora_sharpness);
        Cvar_RegisterVariable(&cl_capturevideo_ogg_vorbis_quality);
 }
 
index 0f2f6fd1bd513d70566d09f15f071ce4db11b7da..389675f80395cf66dceede7da6c3fde86ea1f9d5 100644 (file)
@@ -321,7 +321,7 @@ static void CD_f(cmd_state_t *cmd)
                        return;
                }
                for (n = 1; n <= ret; n++)
-                       strlcpy(remap[n], Cmd_Argv(cmd, n+1), sizeof(*remap));
+                       dp_strlcpy(remap[n], Cmd_Argv(cmd, n+1), sizeof(*remap));
 #endif
                return;
        }
@@ -417,12 +417,12 @@ static void CDAudio_SetVolume (float newvol)
                return;
 
        // If the CD has been muted
-       if (newvol == 0.0f)
+       if (newvol <= 0.0f)
                CDAudio_Pause ();
        else
        {
                // If the CD has been unmuted
-               if (cdvolume == 0.0f)
+               if (cdvolume <= 0.0f)
                        CDAudio_Resume ();
 
                if (faketrack != -1)
@@ -473,7 +473,7 @@ static void CDAudio_StartPlaylist(qbool resume)
                                        break;
                                // if we don't find the desired track, use the first one
                                if (count == 0)
-                                       strlcpy(trackname, com_token, sizeof(trackname));
+                                       dp_strlcpy(trackname, com_token, sizeof(trackname));
                        }
                }
                if (count > 0)
@@ -503,7 +503,7 @@ static void CDAudio_StartPlaylist(qbool resume)
                                                break;
                                        if (listindex == current)
                                        {
-                                               strlcpy(trackname, com_token, sizeof(trackname));
+                                               dp_strlcpy(trackname, com_token, sizeof(trackname));
                                                break;
                                        }
                                }
index 9277ad50964b77614057f9673895fd83a0cfd15b..4556d2cf7ec8292c6b8bd0395945c69c8e102850 100644 (file)
--- a/cl_cmd.c
+++ b/cl_cmd.c
@@ -28,7 +28,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
 #include "cl_collision.h"
 
-cvar_t cl_name = {CF_CLIENT | CF_ARCHIVE | CF_USERINFO, "name", "player", "player name"};
+/// User-visible names of these CF_USERINFO cvars must be matched in CL_SetInfo()!
+cvar_t cl_name = {CF_CLIENT | CF_ARCHIVE | CF_USERINFO, "_cl_name", "player", "player name"};
 cvar_t cl_rate = {CF_CLIENT | CF_ARCHIVE | CF_USERINFO, "rate", "20000", "connection speed"};
 cvar_t cl_rate_burstsize = {CF_CLIENT | CF_ARCHIVE | CF_USERINFO, "rate_burstsize", "1024", "rate control burst size"};
 cvar_t cl_topcolor = {CF_CLIENT | CF_ARCHIVE | CF_USERINFO, "topcolor", "0", "color of your shirt"};
@@ -37,6 +38,7 @@ cvar_t cl_team = {CF_CLIENT | CF_USERINFO | CF_ARCHIVE, "team", "none", "QW team
 cvar_t cl_skin = {CF_CLIENT | CF_USERINFO | CF_ARCHIVE, "skin", "", "QW player skin name (example: base)"};
 cvar_t cl_noaim = {CF_CLIENT | CF_USERINFO | CF_ARCHIVE, "noaim", "1", "QW option to disable vertical autoaim"};
 cvar_t cl_pmodel = {CF_CLIENT | CF_USERINFO | CF_ARCHIVE, "pmodel", "0", "current player model number in nehahra"};
+
 cvar_t r_fixtrans_auto = {CF_CLIENT, "r_fixtrans_auto", "0", "automatically fixtrans textures (when set to 2, it also saves the fixed versions to a fixtrans directory)"};
 
 extern cvar_t rcon_secure;
@@ -113,40 +115,40 @@ void CL_ForwardToServer (const char *s)
                                        if (cl.stats[STAT_ITEMS] & IT_QUAD)
                                        {
                                                if (temp[0])
-                                                       strlcat(temp, " ", sizeof(temp));
-                                               strlcat(temp, "quad", sizeof(temp));
+                                                       dp_strlcat(temp, " ", sizeof(temp));
+                                               dp_strlcat(temp, "quad", sizeof(temp));
                                        }
                                        if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY)
                                        {
                                                if (temp[0])
-                                                       strlcat(temp, " ", sizeof(temp));
-                                               strlcat(temp, "pent", sizeof(temp));
+                                                       dp_strlcat(temp, " ", sizeof(temp));
+                                               dp_strlcat(temp, "pent", sizeof(temp));
                                        }
                                        if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY)
                                        {
                                                if (temp[0])
-                                                       strlcat(temp, " ", sizeof(temp));
-                                               strlcat(temp, "eyes", sizeof(temp));
+                                                       dp_strlcat(temp, " ", sizeof(temp));
+                                               dp_strlcat(temp, "eyes", sizeof(temp));
                                        }
                                        break;
                                case 'w': // weapon status (outputs "SSG:NG:SNG:GL:RL:LG" with the text between : characters omitted if you lack the weapon)
                                        if (cl.stats[STAT_ITEMS] & IT_SUPER_SHOTGUN)
-                                               strlcat(temp, "SSG", sizeof(temp));
-                                       strlcat(temp, ":", sizeof(temp));
+                                               dp_strlcat(temp, "SSG", sizeof(temp));
+                                       dp_strlcat(temp, ":", sizeof(temp));
                                        if (cl.stats[STAT_ITEMS] & IT_NAILGUN)
-                                               strlcat(temp, "NG", sizeof(temp));
-                                       strlcat(temp, ":", sizeof(temp));
+                                               dp_strlcat(temp, "NG", sizeof(temp));
+                                       dp_strlcat(temp, ":", sizeof(temp));
                                        if (cl.stats[STAT_ITEMS] & IT_SUPER_NAILGUN)
-                                               strlcat(temp, "SNG", sizeof(temp));
-                                       strlcat(temp, ":", sizeof(temp));
+                                               dp_strlcat(temp, "SNG", sizeof(temp));
+                                       dp_strlcat(temp, ":", sizeof(temp));
                                        if (cl.stats[STAT_ITEMS] & IT_GRENADE_LAUNCHER)
-                                               strlcat(temp, "GL", sizeof(temp));
-                                       strlcat(temp, ":", sizeof(temp));
+                                               dp_strlcat(temp, "GL", sizeof(temp));
+                                       dp_strlcat(temp, ":", sizeof(temp));
                                        if (cl.stats[STAT_ITEMS] & IT_ROCKET_LAUNCHER)
-                                               strlcat(temp, "RL", sizeof(temp));
-                                       strlcat(temp, ":", sizeof(temp));
+                                               dp_strlcat(temp, "RL", sizeof(temp));
+                                       dp_strlcat(temp, ":", sizeof(temp));
                                        if (cl.stats[STAT_ITEMS] & IT_LIGHTNING)
-                                               strlcat(temp, "LG", sizeof(temp));
+                                               dp_strlcat(temp, "LG", sizeof(temp));
                                        break;
                                default:
                                        // not a recognized macro, print it as-is...
@@ -216,6 +218,32 @@ static void CL_SendCvar_f(cmd_state_t *cmd)
        }
 }
 
+/*
+==================
+CL_Name_f
+
+The logic from div0-stable's Host_Name_f() is now in SV_Name_f().
+==================
+*/
+static void CL_Name_f(cmd_state_t *cmd)
+{
+       char *newNameSource;
+
+       if (Cmd_Argc(cmd) == 1)
+       {
+               Con_Printf("name: \"%s^7\"\n", cl_name.string);
+               return;
+       }
+
+       // in the single-arg case any enclosing quotes shall be stripped
+       newNameSource = (char *)(Cmd_Argc(cmd) == 2 ? Cmd_Argv(cmd, 1) : Cmd_Args(cmd));
+
+       if (strlen(newNameSource) >= MAX_SCOREBOARDNAME) // may as well truncate before networking
+               newNameSource[MAX_SCOREBOARDNAME - 1] = '\0'; // this is fine (cbuf stores length)
+
+       Cvar_SetQuick(&cl_name, newNameSource);
+}
+
 /*
 ==================
 CL_Color_f
@@ -566,7 +594,7 @@ static void CL_Rcon_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
                        ++cls.rcon_trying;
                        if(i >= MAX_RCONS)
                                NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", &cls.rcon_address); // otherwise we'll request the challenge later
-                       strlcpy(cls.rcon_commands[cls.rcon_ringpos], Cmd_Args(cmd), sizeof(cls.rcon_commands[cls.rcon_ringpos]));
+                       dp_strlcpy(cls.rcon_commands[cls.rcon_ringpos], Cmd_Args(cmd), sizeof(cls.rcon_commands[cls.rcon_ringpos]));
                        cls.rcon_addresses[cls.rcon_ringpos] = cls.rcon_address;
                        cls.rcon_timeout[cls.rcon_ringpos] = host.realtime + rcon_secure_challengetimeout.value;
                        cls.rcon_ringpos = (cls.rcon_ringpos + 1) % MAX_RCONS;
@@ -580,7 +608,7 @@ static void CL_Rcon_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
                        if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 24), (unsigned char *) argbuf, (int)strlen(argbuf), (unsigned char *) rcon_password.string, n))
                        {
                                buf[40] = ' ';
-                               strlcpy(buf + 41, argbuf, sizeof(buf) - 41);
+                               dp_strlcpy(buf + 41, argbuf, sizeof(buf) - 41);
                                NetConn_Write(mysocket, buf, 41 + (int)strlen(buf + 41), &cls.rcon_address);
                        }
                }
@@ -611,7 +639,7 @@ static void CL_FullServerinfo_f(cmd_state_t *cmd) // credit: taken from QuakeWor
                return;
        }
 
-       strlcpy (cl.qw_serverinfo, Cmd_Argv(cmd, 1), sizeof(cl.qw_serverinfo));
+       dp_strlcpy (cl.qw_serverinfo, Cmd_Argv(cmd, 1), sizeof(cl.qw_serverinfo));
        InfoString_GetValue(cl.qw_serverinfo, "teamplay", temp, sizeof(temp));
        cl.qw_teamplay = atoi(temp);
 }
@@ -645,7 +673,7 @@ static void CL_FullInfo_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
                if (len >= sizeof(key)) {
                        len = sizeof(key) - 1;
                }
-               strlcpy(key, s, len + 1);
+               dp_strlcpy(key, s, len + 1);
                s += len;
                if (!*s)
                {
@@ -658,7 +686,7 @@ static void CL_FullInfo_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
                if (len >= sizeof(value)) {
                        len = sizeof(value) - 1;
                }
-               strlcpy(value, s, len + 1);
+               dp_strlcpy(value, s, len + 1);
 
                CL_SetInfo(key, value, false, false, false, false);
 
@@ -715,8 +743,16 @@ void CL_InitCommands(void)
 {
        dpsnprintf(cls.userinfo, sizeof(cls.userinfo), "\\name\\player\\team\\none\\topcolor\\0\\bottomcolor\\0\\rate\\10000\\msg\\1\\noaim\\1\\*ver\\dp");
 
+       /* In Quake `name` is a command that concatenates its arguments (quotes unnecessary)
+        * which is expected in most DP-based games.
+        * In QuakeWorld it's a cvar which requires quotes if spaces are used.
+        */
        Cvar_RegisterVariable(&cl_name);
-       Cvar_RegisterVirtual(&cl_name, "_cl_name");
+       if ((0)) // TODO: if (gamemode == GAME_QUAKEWORLD)
+               Cvar_RegisterVirtual(&cl_name, "name");
+       else
+               Cmd_AddCommand(CF_CLIENT, "name", CL_Name_f, "change your player name");
+
        Cvar_RegisterVariable(&cl_rate);
        Cvar_RegisterVirtual(&cl_rate, "_cl_rate");
        Cvar_RegisterVariable(&cl_rate_burstsize);
index 97303e7a92a9775678e2b5b5c7eefaaeba384cb2..2ba9dc499a8b65f9a8e078d59b85b70d0e62ef3a 100644 (file)
@@ -169,7 +169,7 @@ void CL_LinkEdict(prvm_edict_t *ent)
        VectorCopy(mins, PRVM_clientedictvector(ent, absmin));
        VectorCopy(maxs, PRVM_clientedictvector(ent, absmax));
 
-       World_LinkEdict(&cl.world, ent, mins, maxs);
+       World_LinkEdict(&cl.world, ent, mins, maxs, cl_areagrid_link_SOLID_NOT.integer);
 }
 
 int CL_GenericHitSuperContentsMask(const prvm_edict_t *passedict)
@@ -978,7 +978,7 @@ trace_t CL_Cache_TraceLineSurfaces(const vec3_t start, const vec3_t end, int typ
                if (!model)
                        continue;
                // animated models are not suitable for caching
-               if ((&touch->priv.server->frameblend[0] && (touch->priv.server->frameblend[0].lerp != 1.0 || touch->priv.server->frameblend[0].subframe != 0)) || touch->priv.server->skeleton.relativetransforms)
+               if ((touch->priv.server->frameblend[0].lerp != 1.0 || touch->priv.server->frameblend[0].subframe != 0) || touch->priv.server->skeleton.relativetransforms)
                        continue;
                if (type == MOVE_NOMONSTERS && PRVM_clientedictfloat(touch, solid) != SOLID_BSP)
                        continue;
index beab8f84ed84354dc28d5f813e3a77ae4b13895b..a11d63b5ba33f5939dc7de352f4940d2e1b85030 100644 (file)
--- a/cl_demo.c
+++ b/cl_demo.c
@@ -24,7 +24,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 extern cvar_t cl_capturevideo;
 extern cvar_t cl_capturevideo_demo_stop;
 #endif
-int old_vsync = 0;
 
 static void CL_FinishTimeDemo (void);
 
@@ -245,6 +244,13 @@ void CL_ReadDemoMessage(void)
                        }
                }
 
+               /* At signon 1 the cl_begindownloads command starts the world and, if applicable,
+                * boots up CSQC which may be required to parse the next message.
+                * That will be delayed if curl must first (down)load the map.
+                */
+               if (cls.signon == 1 && cl.loadcsqc) // waiting for CL_VM_Init() to be called
+                       return;
+
                // get the next message
                FS_Read(cls.demofile, &cl_message.cursize, 4);
                cl_message.cursize = LittleLong(cl_message.cursize);
@@ -342,6 +348,7 @@ void CL_Record_f(cmd_state_t *cmd)
        int c, track;
        char name[MAX_OSPATH];
        char vabuf[1024];
+       int vabuf_len;
 
        c = Cmd_Argc(cmd);
        if (c != 2 && c != 3 && c != 4)
@@ -375,12 +382,15 @@ void CL_Record_f(cmd_state_t *cmd)
                track = -1;
 
        // get the demo name
-       strlcpy (name, Cmd_Argv(cmd, 1), sizeof (name));
+       dp_strlcpy (name, Cmd_Argv(cmd, 1), sizeof (name));
        FS_DefaultExtension (name, ".dem", sizeof (name));
 
        // start the map up
        if (c > 2)
-               Cmd_ExecuteString ( cmd, va(vabuf, sizeof(vabuf), "map %s", Cmd_Argv(cmd, 2)), src_local, false);
+       {
+               vabuf_len = dpsnprintf(vabuf, sizeof(vabuf), "map %s", Cmd_Argv(cmd, 2));
+               Cmd_ExecuteString(cmd, vabuf, vabuf_len, src_local, false);
+       }
 
        // open the demo file
        Con_Printf("recording to %s.\n", name);
@@ -390,7 +400,7 @@ void CL_Record_f(cmd_state_t *cmd)
                Con_Print(CON_ERROR "ERROR: couldn't open.\n");
                return;
        }
-       strlcpy(cls.demoname, name, sizeof(cls.demoname));
+       dp_strlcpy(cls.demoname, name, sizeof(cls.demoname));
 
        cls.forcetrack = track;
        FS_Printf(cls.demofile, "%i\n", cls.forcetrack);
@@ -408,7 +418,7 @@ void CL_PlayDemo(const char *demo)
        qfile_t *f;
 
        // open the demo file
-       strlcpy (name, demo, sizeof (name));
+       dp_strlcpy (name, demo, sizeof (name));
        FS_DefaultExtension (name, ".dem", sizeof (name));
        f = FS_OpenVirtualFile(name, false);
        if (!f)
@@ -430,7 +440,7 @@ void CL_PlayDemo(const char *demo)
 
        Con_Printf("Playing demo %s.\n", name);
        cls.demofile = f;
-       strlcpy(cls.demoname, name, sizeof(cls.demoname));
+       dp_strlcpy(cls.demoname, name, sizeof(cls.demoname));
 
        cls.demoplayback = true;
        cls.state = ca_connected;
@@ -510,7 +520,7 @@ static void CL_FinishTimeDemo (void)
        fpsmax = cls.td_onesecondmaxfps;
        // LadyHavoc: timedemo now prints out 7 digits of fraction, and min/avg/max
        Con_Printf("%i frames %5.7f seconds %5.7f fps, one-second fps min/avg/max: %.0f %.0f %.0f (%i seconds)\n", frames, time, totalfpsavg, fpsmin, fpsavg, fpsmax, cls.td_onesecondavgcount);
-       Log_Printf("benchmark.log", "date %s | enginedate %s | demo %s | commandline %s | run %d | result %i frames %5.7f seconds %5.7f fps, one-second fps min/avg/max: %.0f %.0f %.0f (%i seconds)\n", Sys_TimeString("%Y-%m-%d %H:%M:%S"), buildstring, cls.demoname, cmdline.string, benchmark_runs + 1, frames, time, totalfpsavg, fpsmin, fpsavg, fpsmax, cls.td_onesecondavgcount);
+       Log_Printf("benchmark.log", "date %s | enginedate %s | demo %s | commandline %s | run %d | result %i frames %5.7f seconds %5.7f fps, one-second fps min/avg/max: %.0f %.0f %.0f (%i seconds)\n", Sys_TimeString("%Y-%m-%d %H:%M:%S"), engineversion, cls.demoname, cmdline.string, benchmark_runs + 1, frames, time, totalfpsavg, fpsmin, fpsavg, fpsmax, cls.td_onesecondavgcount);
        if (Sys_CheckParm("-benchmark"))
        {
                ++benchmark_runs;
@@ -586,6 +596,9 @@ static void CL_FinishTimeDemo (void)
                else
                        host.state = host_shutdown;
        }
+
+       // Might need to re-enable vsync
+       Cvar_Callback(&vid_vsync);
 }
 
 /*
@@ -618,6 +631,9 @@ void CL_TimeDemo_f(cmd_state_t *cmd)
        cls.timedemo = host.restless = true;
        cls.td_frames = -2;             // skip the first frame
        cls.demonum = -1;               // stop demo loop
+
+       // Might need to disable vsync
+       Cvar_Callback(&vid_vsync);
 }
 
 /*
@@ -650,7 +666,7 @@ static void CL_Startdemos_f(cmd_state_t *cmd)
        Con_DPrintf("%i demo(s) in loop\n", c);
 
        for (i=1 ; i<c+1 ; i++)
-               strlcpy (cls.demos[i-1], Cmd_Argv(cmd, i), sizeof (cls.demos[i-1]));
+               dp_strlcpy (cls.demos[i-1], Cmd_Argv(cmd, i), sizeof (cls.demos[i-1]));
 
        // LadyHavoc: clear the remaining slots
        for (;i <= MAX_DEMOS;i++)
index da3104fdac12ea2f9455a8802c6fde0833cf3561..f41c4c91b757bddecfaaac8e7f3df427c4fd318b 100644 (file)
@@ -211,7 +211,7 @@ static void IN_BestWeapon_Register(const char *name, int impulse, int weaponbit,
                Con_Printf("no slot left for weapon definition; increase IN_BESTWEAPON_MAX\n");
                return; // sorry
        }
-       strlcpy(in_bestweapon_info[i].name, name, sizeof(in_bestweapon_info[i].name));
+       dp_strlcpy(in_bestweapon_info[i].name, name, sizeof(in_bestweapon_info[i].name));
        in_bestweapon_info[i].impulse = impulse;
        if(weaponbit != -1)
                in_bestweapon_info[i].weaponbit = weaponbit;
@@ -399,7 +399,7 @@ cvar_t in_pitch_min = {CF_CLIENT, "in_pitch_min", "-90", "how far you can aim up
 cvar_t in_pitch_max = {CF_CLIENT, "in_pitch_max", "90", "how far you can aim downward (quake used 80)"};
 
 cvar_t m_filter = {CF_CLIENT | CF_ARCHIVE, "m_filter","0", "smoothes mouse movement, less responsive but smoother aiming"}; 
-cvar_t m_accelerate = {CF_CLIENT | CF_ARCHIVE, "m_accelerate","1", "linear mouse acceleration factor (set to 1 to disable the linear acceleration and use only the power acceleration; set to 0 to disable all acceleration)"};
+cvar_t m_accelerate = {CF_CLIENT | CF_ARCHIVE, "m_accelerate","1", "linear mouse acceleration factor (set to 1 to disable the linear acceleration and use only the power or natural acceleration; set to 0 to disable all acceleration)"};
 cvar_t m_accelerate_minspeed = {CF_CLIENT | CF_ARCHIVE, "m_accelerate_minspeed","5000", "below this speed in px/s, no acceleration is done, with a linear slope between (applied only on linear acceleration)"};
 cvar_t m_accelerate_maxspeed = {CF_CLIENT | CF_ARCHIVE, "m_accelerate_maxspeed","10000", "above this speed in px/s, full acceleration is done, with a linear slope between (applied only on linear acceleration)"};
 cvar_t m_accelerate_filter = {CF_CLIENT | CF_ARCHIVE, "m_accelerate_filter","0", "linear mouse acceleration factor filtering lowpass constant in seconds (set to 0 for no filtering)"};
@@ -407,6 +407,9 @@ cvar_t m_accelerate_power_offset = {CF_CLIENT | CF_ARCHIVE, "m_accelerate_power_
 cvar_t m_accelerate_power = {CF_CLIENT | CF_ARCHIVE, "m_accelerate_power","2", "acceleration power (must be above 1 to be useful)"};
 cvar_t m_accelerate_power_senscap = {CF_CLIENT | CF_ARCHIVE, "m_accelerate_power_senscap", "0", "maximum acceleration factor generated by power acceleration; use 0 for unbounded"};
 cvar_t m_accelerate_power_strength = {CF_CLIENT | CF_ARCHIVE, "m_accelerate_power_strength", "0", "strength of the power mouse acceleration effect"};
+cvar_t m_accelerate_natural_strength = {CF_CLIENT | CF_ARCHIVE, "m_accelerate_natural_strength", "0", "How quickly the accelsensitivity approaches the m_accelerate_natural_accelsenscap, values are compressed between 0 and 1 but higher numbers are allowed"};
+cvar_t m_accelerate_natural_accelsenscap = {CF_CLIENT | CF_ARCHIVE, "m_accelerate_natural_accelsenscap", "0", "Horizontal asymptote that sets the maximum value for the natural mouse acceleration curve, value 2, for example, means that the maximum sensitivity is 2 times the base sensitivity"};
+cvar_t m_accelerate_natural_offset = {CF_CLIENT | CF_ARCHIVE, "m_accelerate_natural_offset", "0", "below this speed in px/ms, no natural acceleration is done"};
 
 cvar_t cl_netfps = {CF_CLIENT | CF_ARCHIVE, "cl_netfps","72", "how many input packets to send to server each second"};
 cvar_t cl_netrepeatinput = {CF_CLIENT | CF_ARCHIVE, "cl_netrepeatinput", "1", "how many packets in a row can be lost without movement issues when using cl_movement (technically how many input messages to repeat in each packet that have not yet been acknowledged by the server), only affects DP7 and later servers (Quake uses 0, QuakeWorld uses 2, and just for comparison Quake3 uses 1)"};
@@ -524,7 +527,7 @@ void CL_Input (void)
        IN_Move ();
 
        // send mouse move to csqc
-       if (cl.csqc_loaded && cl_csqc_generatemousemoveevents.integer)
+       if (CLVM_prog->loaded && cl_csqc_generatemousemoveevents.integer)
        {
                if (cl.csqc_wantsmousemove)
                {
@@ -544,7 +547,7 @@ void CL_Input (void)
                }
        }
 
-       // apply m_accelerate if it is on
+       // apply m_accelerate if it is on
        if(m_accelerate.value > 0)
        {
                float mouse_deltadist = sqrtf(in_mouse_x * in_mouse_x + in_mouse_y * in_mouse_y);
@@ -552,9 +555,9 @@ void CL_Input (void)
                static float averagespeed = 0;
                float f, mi, ma;
                if(m_accelerate_filter.value > 0)
-                       f = bound(0, cl.realframetime / m_accelerate_filter.value, 1);
+                       f = bound(0, cl.realframetime / m_accelerate_filter.value, 1);
                else
-                       f = 1;
+                       f = 1;
                averagespeed = speed * f + averagespeed * (1 - f);
 
                // Note: this check is technically unnecessary, as everything in here cancels out if it is zero.
@@ -589,7 +592,7 @@ void CL_Input (void)
                {
                        // Then do Quake Live-style power acceleration.
                        // Note that this behavior REPLACES the usual
-                       // sensitivity, so we apply it but then dividie by
+                       // sensitivity, so we apply it but then divide by
                        // sensitivity.value so that the later multiplication
                        // restores it again.
                        float accelsens = 1.0f;
@@ -620,7 +623,25 @@ void CL_Input (void)
                        {
                                // TODO: How does this interact with sensitivity changes? Is this intended?
                                // Currently: senscap is in absolute sensitivity units, so if senscap < sensitivity, it overrides.
-                               accelsens = m_accelerate_power_senscap.value * inv_sensitivity;
+                               accelsens = m_accelerate_power_senscap.value * inv_sensitivity;
+                       }
+
+                       in_mouse_x *= accelsens;
+                       in_mouse_y *= accelsens;
+               }
+
+               if (m_accelerate_natural_strength.value > 0.0f && m_accelerate_natural_accelsenscap.value >= 0.0f)
+               {
+                       float accelsens = 1.0f;
+                       float adjusted_speed_pxms = (averagespeed * 0.001f - m_accelerate_natural_offset.value);
+
+                       if (adjusted_speed_pxms > 0 && m_accelerate_natural_accelsenscap.value != 1.0f)
+                       {
+                               float adjusted_accelsenscap = m_accelerate_natural_accelsenscap.value - 1.0f;
+                               // This equation is made to multiply the sensitivity for a factor between 1 and m_accelerate_natural_accelsenscap
+                               // this means there is no need to divide it for the sensitivity.value as the whole
+                               // expression needs to be multiplied by the sensitivity at the end instead of only having the sens multiplied
+                               accelsens += (adjusted_accelsenscap - adjusted_accelsenscap * exp( - ((adjusted_speed_pxms * m_accelerate_natural_strength.value) / fabs(adjusted_accelsenscap) )));
                        }
 
                        in_mouse_x *= accelsens;
@@ -1517,8 +1538,8 @@ void CL_UpdateMoveVars(void)
        else
        {
                cl.moveflags = 0;
-               cl.movevars_ticrate = (cls.demoplayback ? 1.0f : host_timescale.value) / bound(10.0f, cl_netfps.value, 1000.0f);
-               cl.movevars_timescale = (cls.demoplayback ? 1.0f : host_timescale.value);
+               cl.movevars_ticrate = 0; // bones_was_here: no guessing, unavailable ticrate triggers better fallbacks
+               cl.movevars_timescale = (cls.demoplayback || host_timescale.value <= 0) ? 1.0f : host_timescale.value;
                cl.movevars_gravity = sv_gravity.value;
                cl.movevars_stopspeed = cl_movement_stopspeed.value;
                cl.movevars_maxspeed = cl_movement_maxspeed.value;
@@ -1756,7 +1777,8 @@ void CL_SendMove(void)
        usercmd_t *cmd;
        sizebuf_t buf;
        unsigned char data[1024];
-       float packettime;
+       float packettime, sv_frametime, lag, frames_per_tic;
+       qbool opportune_moment;
        qbool quemove;
        qbool important;
 
@@ -1785,7 +1807,7 @@ void CL_SendMove(void)
        if (in_button7.state  & 3) bits |=  64;
        if (in_button8.state  & 3) bits |= 128;
        if (in_use.state      & 3) bits |= 256;
-       if (key_dest != key_game || key_consoleactive) bits |= 512;
+       if (key_dest != key_game || key_consoleactive || !vid_activewindow) bits |= 512;
        if (cl_prydoncursor.integer > 0) bits |= 1024;
        if (in_button9.state  & 3)  bits |=   2048;
        if (in_button10.state  & 3) bits |=   4096;
@@ -1872,8 +1894,14 @@ void CL_SendMove(void)
        if (quemove)
                cl.movecmd[0] = cl.cmd;
 
-       // don't predict more than 200fps
-       if (cl.timesincepacket >= 0.005)
+       /* Accumulating cl.realframetime to prevent low packet rates,
+        * previously with cl_maxfps == cl_netfps it did not send every frame because
+        * host.realtime - cl.lastpackettime was often well below (or above) cl_packetinterval.
+        */
+       cl.timesincepacket += cl.realframetime;
+
+       // don't predict more than 256fps
+       if (cl.timesincepacket >= 1/256)
                cl.movement_replay = true; // redo the prediction
 
        // now decide whether to actually send this move
@@ -1886,32 +1914,46 @@ void CL_SendMove(void)
        // don't send too often or else network connections can get clogged by a
        // high renderer framerate
        packettime = 1.0f / bound(10.0f, cl_netfps.value, 1000.0f);
-       if (cl.movevars_timescale && cl.movevars_ticrate)
+       if (cl.movevars_ticrate)
        {
-               // try to ensure at least 1 packet per server frame
-               // and apply soft limit of 2, hard limit < 4 (packettime reduced further below)
-               float maxtic = cl.movevars_ticrate / cl.movevars_timescale;
-               packettime = bound(maxtic * 0.5f, packettime, maxtic);
+               packettime = bound(cl.movevars_ticrate * 0.5f, packettime, cl.movevars_ticrate);
+               sv_frametime = cl.movevars_ticrate;
        }
-       // bones_was_here: reduce packettime to (largest multiple of realframetime) <= packettime
-       // prevents packet rates lower than cl_netfps or server frame rate
-       // eg: cl_netfps 60 and cl_maxfps 250 would otherwise send only 50 netfps
-       // with this line that config sends 62.5 netfps
-       // (this causes it to emit packets at a steady beat)
-       packettime = floor(packettime / (float)cl.realframetime) * (float)cl.realframetime;
+       else // fallback, may be affected by server->client network problems
+               sv_frametime = (cl.mtime[0] - cl.mtime[1]) / cl.movevars_timescale;
 
        // always send if buttons changed or an impulse is pending
        // even if it violates the rate limit!
        important = (cl.cmd.impulse || (cl_netimmediatebuttons.integer && cl.cmd.buttons != cl.movecmd[1].buttons));
 
-       // don't send too often (cl_netfps), allowing a small margin for float error
-       // bones_was_here: accumulate realframetime to prevent low packet rates
-       // previously with cl_maxfps == cl_netfps it did not send every frame as
-       // host.realtime - cl.lastpackettime was often well below (or above) packettime
-       if (!important && cl.timesincepacket < packettime * 0.99999f)
+       lag = cl.mtime[0] - cl.cmd.time;
+       frames_per_tic = sv_frametime / cl.realframetime;
+//     opportune_moment = lag <= cl.realframetime * 2; // FAIL: can miss the moment with uncapped fps
+//     opportune_moment = lag <= cl.realframetime * frames_per_tic * 0.5; // FAIL: too early at high fps, reducing multi causes misses at moderate fps
+       opportune_moment = lag <= cl.realframetime * (frames_per_tic <= 1 ? 1 : sqrt(frames_per_tic)); // perfect
+
+       // Two methods for deciding when to send
+       if (!important)
        {
-               cl.timesincepacket += cl.realframetime;
-               return;
+               // Traditional time interval, now used as fallback
+               if (sv_frametime <= 0 || lag > sv_frametime || lag < 0) // unknown ticrate || lag/PL || still connecting
+               {
+                       if (cl.timesincepacket < packettime * 0.99999f)
+                       {
+//                             Con_Printf("^6moveft %f realft %f lag %f tic %f inputsince %d\n", cl.cmd.frametime, cl.realframetime, lag, sv_frametime, cl.opt_inputs_since_update);
+                               return;
+                       }
+               }
+               // Server-synchronised, for better pings
+               // Sends at least once per server frame
+               else // cl.opt_inputs_since_update is usable
+               {
+                       if (!opportune_moment || cl.opt_inputs_since_update >= sv_frametime / packettime)
+                       {
+//                             Con_Printf("^1moveft %f realft %f lag %f tic %f inputsince %d\n", cl.cmd.frametime, cl.realframetime, lag, sv_frametime, cl.opt_inputs_since_update);
+                               return;
+                       }
+               }
        }
 
        // don't choke the connection with packets (obey rate limit)
@@ -1922,8 +1964,13 @@ void CL_SendMove(void)
        if (!NetConn_CanSend(cls.netcon) && !important)
                return;
 
+//     Con_Printf("%smoveft %f realft %f lag %f tic %f inputsince %d important %d\n", (lag < 0.0005 || !opportune_moment) ? "^3" : "^2", cl.cmd.frametime, cl.realframetime, lag, sv_frametime, cl.opt_inputs_since_update, important);
+
+       if (opportune_moment) // this check is needed for optimal timing especially with cl_netimmediatebuttons
+               ++cl.opt_inputs_since_update;
+
        // reset the packet timing accumulator
-       cl.timesincepacket = cl.realframetime;
+       cl.timesincepacket = 0;
 
        buf.maxsize = sizeof(data);
        buf.cursize = 0;
@@ -2280,6 +2327,9 @@ void CL_InitInput (void)
        Cvar_RegisterVariable(&m_accelerate_power_offset);
        Cvar_RegisterVariable(&m_accelerate_power_senscap);
        Cvar_RegisterVariable(&m_accelerate_power_strength);
+       Cvar_RegisterVariable(&m_accelerate_natural_offset);
+       Cvar_RegisterVariable(&m_accelerate_natural_accelsenscap);
+       Cvar_RegisterVariable(&m_accelerate_natural_strength);
 
        Cvar_RegisterVariable(&cl_netfps);
        Cvar_RegisterVariable(&cl_netrepeatinput);
index 744ef854f7daf62c0deaafa34c37abb91e59e10e..b4d7760c328b40db27b44b224684aa11557fc521 100644 (file)
--- a/cl_main.c
+++ b/cl_main.c
@@ -104,9 +104,13 @@ cvar_t cl_minfps_qualityhysteresis = {CF_CLIENT | CF_ARCHIVE, "cl_minfps_quality
 cvar_t cl_minfps_qualitystepmax = {CF_CLIENT | CF_ARCHIVE, "cl_minfps_qualitystepmax", "0.1", "maximum quality change in a single frame"};
 cvar_t cl_minfps_force = {CF_CLIENT, "cl_minfps_force", "0", "also apply quality reductions in timedemo/capturevideo"};
 cvar_t cl_maxfps = {CF_CLIENT | CF_ARCHIVE, "cl_maxfps", "0", "maximum fps cap, 0 = unlimited, if game is running faster than this it will wait before running another frame (useful to make cpu time available to other programs)"};
-cvar_t cl_maxfps_alwayssleep = {CF_CLIENT | CF_ARCHIVE, "cl_maxfps_alwayssleep","1", "gives up some processing time to other applications each frame, value in milliseconds, disabled if cl_maxfps is 0"};
+cvar_t cl_maxfps_alwayssleep = {CF_CLIENT | CF_ARCHIVE, "cl_maxfps_alwayssleep", "0", "gives up some processing time to other applications each frame, value in milliseconds, disabled if a timedemo is running"};
 cvar_t cl_maxidlefps = {CF_CLIENT | CF_ARCHIVE, "cl_maxidlefps", "20", "maximum fps cap when the game is not the active window (makes cpu time available to other programs"};
 
+cvar_t cl_areagrid_link_SOLID_NOT = {CF_CLIENT, "cl_areagrid_link_SOLID_NOT", "1", "set to 0 to prevent SOLID_NOT entities from being linked to the area grid, and unlink any that are already linked (in the code paths that would otherwise link them), for better performance"};
+cvar_t cl_gameplayfix_nudgeoutofsolid_separation = {CF_CLIENT, "cl_gameplayfix_nudgeoutofsolid_separation", "0.03125", "keep objects this distance apart to prevent collision issues on seams"};
+
+
 client_static_t        cls;
 client_state_t cl;
 
@@ -253,7 +257,7 @@ void CL_SetInfo(const char *key, const char *value, qbool send, qbool allowstark
                        MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd);
                        MSG_WriteString(&cls.netcon->message, va(vabuf, sizeof(vabuf), "setinfo \"%s\" \"%s\"", key, value));
                }
-               else if (!strcasecmp(key, "name"))
+               else if (!strcasecmp(key, "_cl_name") || !strcasecmp(key, "name"))
                {
                        MSG_WriteByte(&cls.netcon->message, clc_stringcmd);
                        MSG_WriteString(&cls.netcon->message, va(vabuf, sizeof(vabuf), "name \"%s\"", value));
@@ -387,11 +391,13 @@ void CL_DisconnectEx(qbool kicked, const char *fmt, ... )
 
        Con_DPrintf("CL_Disconnect\n");
 
-    Cvar_SetValueQuick(&csqc_progcrc, -1);
+       Cvar_SetValueQuick(&csqc_progcrc, -1);
        Cvar_SetValueQuick(&csqc_progsize, -1);
        CL_VM_ShutDown();
-// stop sounds (especially looping!)
-       S_StopAllSounds ();
+       // stop sounds (especially looping!)
+       S_StopAllSounds();
+       // prevent dlcache assets from this server from interfering with the next one
+       FS_UnloadPacks_dlcache();
 
        cl.parsingtextexpectingpingforscores = 0; // just in case no reply has come yet
 
@@ -433,6 +439,8 @@ void CL_DisconnectEx(qbool kicked, const char *fmt, ... )
                                MSG_WriteByte(&buf, clc_disconnect);
                                if(cls.protocol == PROTOCOL_DARKPLACES8)
                                        MSG_WriteString(&buf, reason);
+                               // DP8 TODO: write a simpler func that Sys_HandleCrash() calls
+                               // to send a disconnect message indicating we crashed
                        }
                        NetConn_SendUnreliableMessage(cls.netcon, &buf, cls.protocol, 10000, 0, false);
                        NetConn_SendUnreliableMessage(cls.netcon, &buf, cls.protocol, 10000, 0, false);
@@ -441,16 +449,20 @@ void CL_DisconnectEx(qbool kicked, const char *fmt, ... )
 
                NetConn_Close(cls.netcon);
                cls.netcon = NULL;
-               if(fmt)
-                       Con_Printf("Disconnect: %s\n", reason);
+
+               // It's possible for a server to disconnect a player with an empty reason
+               // which is checked here rather than above so we don't print "Disconnect by user".
+               if(fmt && reason[0] != '\0')
+                       dpsnprintf(cl_connect_status, sizeof(cl_connect_status), "Disconnect: %s", reason);
                else
-                       Con_Printf("Disconnected\n");
+                       dp_strlcpy(cl_connect_status, "Disconnected", sizeof(cl_connect_status));
+               Con_Printf("%s\n", cl_connect_status);
        }
        cls.state = ca_disconnected;
        cl.islocalgame = false;
-
-       cls.demoplayback = cls.timedemo = host.restless = false;
        cls.signon = 0;
+       cls.demoplayback = cls.timedemo = host.restless = false;
+       Cvar_Callback(&vid_vsync); // might need to re-enable vsync
 
        Cvar_Callback(&cl_netport);
 
@@ -486,7 +498,7 @@ static void CL_Reconnect_f(cmd_state_t *cmd)
                if (temp[0])
                        CL_EstablishConnection(temp, -1);
                else
-                       Con_Printf("Reconnect to what server?  (you have not connected to a server yet)\n");
+                       Con_Printf(CON_WARN "Reconnect to what server?  (you have not connected to a server yet)\n");
                return;
        }
        // if connected, do something based on protocol
@@ -566,18 +578,13 @@ void CL_EstablishConnection(const char *address, int firstarg)
        if (Sys_CheckParm("-benchmark"))
                return;
 
-       // clear menu's connect error message
-#ifdef CONFIG_MENU
-       M_Update_Return_Reason("");
-#endif
-
        // make sure the client ports are open before attempting to connect
        NetConn_UpdateSockets();
 
        if (LHNETADDRESS_FromString(&cls.connect_address, address, 26000) && (cls.connect_mysocket = NetConn_ChooseClientSocketForAddress(&cls.connect_address)))
        {
                cls.connect_trying = true;
-               cls.connect_remainingtries = 3;
+               cls.connect_remainingtries = 10;
                cls.connect_nextsendtime = 0;
 
                // only NOW, set connect_userinfo
@@ -595,16 +602,13 @@ void CL_EstablishConnection(const char *address, int firstarg)
                        *cls.connect_userinfo = 0;
                }
 
-#ifdef CONFIG_MENU
-               M_Update_Return_Reason("Trying to connect...");
-#endif
+               dp_strlcpy(cl_connect_status, "Connect: pending...", sizeof(cl_connect_status));
+               SCR_BeginLoadingPlaque(false);
        }
        else
        {
-               Con_Print("Unable to find a suitable network socket to connect to server.\n");
-#ifdef CONFIG_MENU
-               M_Update_Return_Reason("No network");
-#endif
+               Con_Printf(CON_ERROR "Connect: failed, unable to find a network socket suitable to reach %s\n", address);
+               dp_strlcpy(cl_connect_status, "Connect: failed, no network", sizeof(cl_connect_status));
        }
 }
 
@@ -886,7 +890,7 @@ void CL_AllocLightFlash(entity_render_t *ent, matrix4x4_t *matrix, float radius,
                dl->die = 0;
        dl->cubemapname[0] = 0;
        if (cubemapname && cubemapname[0])
-               strlcpy(dl->cubemapname, cubemapname, sizeof(dl->cubemapname));
+               dp_strlcpy(dl->cubemapname, cubemapname, sizeof(dl->cubemapname));
        dl->style = style;
        dl->shadow = shadowenable;
        dl->corona = corona;
@@ -1168,6 +1172,7 @@ static void CL_UpdateNetworkEntity(entity_t *e, int recursionlimit, qbool interp
        // someone or watching a cutscene of some sort
        if (cl_nolerp.integer || cls.timedemo)
                interpolate = false;
+
        if (e == cl.entities + cl.playerentity && cl.movement_predicted && (!cl.fixangle[1] || !cl.fixangle[0]))
        {
                VectorCopy(cl.movement_origin, origin);
@@ -2086,7 +2091,7 @@ void CL_UpdateWorld(void)
                CL_UpdateViewModel();
 
                // when csqc is loaded, it will call this in CSQC_UpdateView
-               if (!cl.csqc_loaded)
+               if (!CLVM_prog->loaded || CLVM_prog->flag & PRVM_CSQC_SIMPLE)
                {
                        // clear the CL_Mesh_Scene() used for some engine effects
                        CL_MeshEntities_Scene_Clear();
@@ -2155,7 +2160,7 @@ static void CL_Fog_HeightTexture_f(cmd_state_t *cmd)
        r_refdef.fog_end = atof(Cmd_Argv(cmd, 7));
        r_refdef.fog_height = atof(Cmd_Argv(cmd, 8));
        r_refdef.fog_fadedepth = atof(Cmd_Argv(cmd, 9));
-       strlcpy(r_refdef.fog_height_texturename, Cmd_Argv(cmd, 10), sizeof(r_refdef.fog_height_texturename));
+       dp_strlcpy(r_refdef.fog_height_texturename, Cmd_Argv(cmd, 10), sizeof(r_refdef.fog_height_texturename));
 }
 
 
@@ -2220,7 +2225,7 @@ void CL_Locs_FindLocationName(char *buffer, size_t buffersize, vec3_t point)
        cl_locnode_t *loc;
        loc = CL_Locs_FindNearest(point);
        if (loc)
-               strlcpy(buffer, loc->name, buffersize);
+               dp_strlcpy(buffer, loc->name, buffersize);
        else
                dpsnprintf(buffer, buffersize, "LOC=%.0f:%.0f:%.0f", point[0], point[1], point[2]);
 }
@@ -2549,7 +2554,7 @@ static void CL_MeshEntities_Shutdown(void)
        }
 }
 
-static void CL_MeshEntities_Init(void)
+void CL_MeshEntities_Init(void)
 {
        int i;
        entity_t *ent;
@@ -2585,7 +2590,7 @@ static void CL_MeshEntities_Init(void)
                CL_UpdateRenderEntity(&ent->render);
        }
        cl_meshentities[MESH_UI].render.flags = RENDER_NOSELFSHADOW;
-       R_RegisterModule("cl_meshentities", CL_MeshEntities_Start, CL_MeshEntities_Shutdown, CL_MeshEntities_Restart, CL_MeshEntities_Restart, CL_MeshEntities_Restart);
+       R_RegisterModule("CL_MeshEntities", CL_MeshEntities_Start, CL_MeshEntities_Shutdown, CL_MeshEntities_Restart, CL_MeshEntities_Restart, CL_MeshEntities_Restart);
 }
 
 void CL_MeshEntities_Scene_Clear(void)
@@ -2790,27 +2795,27 @@ void CL_StartVideo(void)
                NetConn_UpdateSockets();
 #endif
                VID_Start();
-               CDAudio_Startup();
        }
 }
 
 extern cvar_t host_framerate;
 extern cvar_t host_speeds;
-
+extern qbool serverlist_querystage;
 double CL_Frame (double time)
 {
        static double clframetime;
        static double cl_timer = 0;
        static double time1 = 0, time2 = 0, time3 = 0;
        int pass1, pass2, pass3;
+       float maxfps;
 
        CL_VM_PreventInformationLeaks();
 
-       // get new key events
-       Key_EventQueue_Unblock();
-       SndSys_SendKeyEvents();
-       Sys_SendKeyEvents();
-
+       /*
+        * If the accumulator hasn't become positive, don't
+        * run the frame. Everything that happens before this
+        * point will happen even if we're sleeping this frame.
+        */
        if((cl_timer += time) < 0)
                return cl_timer;
 
@@ -2818,7 +2823,11 @@ double CL_Frame (double time)
        if (cl_timer > 0.1)
                cl_timer = 0.1;
 
-       if (cls.state != ca_dedicated && (cl_timer > 0 || cls.timedemo || ((vid_activewindow ? cl_maxfps : cl_maxidlefps).value < 1)))
+       // Run at full speed when querying servers, compared to waking up early to parse
+       // this is simpler and gives pings more representative of what can be expected when playing.
+       maxfps = (vid_activewindow || serverlist_querystage ? cl_maxfps : cl_maxidlefps).value;
+
+       if (cls.state != ca_dedicated && (cl_timer > 0 || cls.timedemo || maxfps <= 0))
        {
                R_TimeReport("---");
                Collision_Cache_NewFrame();
@@ -2827,7 +2836,6 @@ double CL_Frame (double time)
                // decide the simulation time
                if (cls.capturevideo.active)
                {
-                       //***
                        if (cls.capturevideo.realtime)
                                clframetime = cl.realframetime = max(time, 1.0 / cls.capturevideo.framerate);
                        else
@@ -2836,21 +2844,19 @@ double CL_Frame (double time)
                                cl.realframetime = max(time, clframetime);
                        }
                }
-               else if (vid_activewindow && cl_maxfps.value >= 1 && !cls.timedemo)
-
-#else
-               if (vid_activewindow && cl_maxfps.value >= 1 && !cls.timedemo)
+               else
 #endif
                {
-                       clframetime = cl.realframetime = max(cl_timer, 1.0 / cl_maxfps.value);
-                       // when running slow, we need to sleep to keep input responsive
-                       if (cl_maxfps_alwayssleep.value > 0)
-                               Sys_Sleep((int)bound(0, cl_maxfps_alwayssleep.value * 1000, 100000));
+                       if (maxfps <= 0 || cls.timedemo)
+                               clframetime = cl.realframetime = cl_timer;
+                       else
+                               // networking assumes at least 10fps
+                               clframetime = cl.realframetime = bound(cl_timer, 1 / maxfps, 0.1);
+
+                       // on some legacy systems, we need to sleep to keep input responsive
+                       if (cl_maxfps_alwayssleep.value > 0 && !cls.timedemo)
+                               Sys_Sleep(min(cl_maxfps_alwayssleep.value / 1000, 0.05));
                }
-               else if (!vid_activewindow && cl_maxidlefps.value >= 1 && !cls.timedemo)
-                       clframetime = cl.realframetime = max(cl_timer, 1.0 / cl_maxidlefps.value);
-               else
-                       clframetime = cl.realframetime = cl_timer;
 
                // apply slowmo scaling
                clframetime *= cl.movevars_timescale;
@@ -2966,7 +2972,6 @@ void CL_Shutdown (void)
                MR_Shutdown();
 #endif
 
-       CDAudio_Shutdown ();
        S_Terminate ();
        
        R_Modules_Shutdown();
@@ -2978,7 +2983,6 @@ void CL_Shutdown (void)
        CL_MeshEntities_Shutdown();
 
        Key_Shutdown();
-       S_Shutdown();
 
        Mem_FreePool (&cls.permanentmempool);
        Mem_FreePool (&cls.levelmempool);
@@ -3010,7 +3014,6 @@ void CL_Init (void)
                VID_Init();
                Render_Init();
                S_Init();
-               CDAudio_Init();
                Key_Init();
                V_Init();
 
@@ -3100,7 +3103,8 @@ void CL_Init (void)
 
                // for QW connections
                Cvar_RegisterVariable(&qport);
-               Cvar_SetValueQuick(&qport, (rand() * RAND_MAX + rand()) & 0xffff);
+               // multiplying by RAND_MAX necessary for Windows, for which RAND_MAX is only 32767.
+               Cvar_SetValueQuick(&qport, ((unsigned int)rand() * RAND_MAX + (unsigned int)rand()) & 0xffff);
 
                Cmd_AddCommand(CF_CLIENT, "timerefresh", CL_TimeRefresh_f, "turn quickly and print rendering statistcs");
 
@@ -3126,14 +3130,16 @@ void CL_Init (void)
                Cvar_RegisterVariable (&cl_maxfps_alwayssleep);
                Cvar_RegisterVariable (&cl_maxidlefps);
 
+               Cvar_RegisterVariable (&cl_areagrid_link_SOLID_NOT);
+               Cvar_RegisterVariable (&cl_gameplayfix_nudgeoutofsolid_separation);
+
                CL_Parse_Init();
                CL_Particles_Init();
                CL_Screen_Init();
-               CL_MeshEntities_Init();
 
                CL_Video_Init();
 
-               NetConn_UpdateSockets_Client();
+               Cvar_Callback(&cl_netport);
 
                host.hook.ConnectLocal = CL_EstablishConnection_Local;
                host.hook.Disconnect = CL_DisconnectEx;
index ecd250fc56da0f21100b204b00e5ff74cc9a1232..dbeda3864f362d9fc8721b9b4822841b138203ab 100644 (file)
@@ -184,6 +184,8 @@ cvar_t cl_readpicture_force = {CF_CLIENT, "cl_readpicture_force", "0", "when ena
 #define RIC_GUNSHOTQUAD        2
 cvar_t cl_sound_ric_gunshot = {CF_CLIENT, "cl_sound_ric_gunshot", "0", "specifies if and when the related cl_sound_ric and cl_sound_tink sounds apply to TE_GUNSHOT/TE_GUNSHOTQUAD, 0 = no sound, 1 = TE_GUNSHOT, 2 = TE_GUNSHOTQUAD, 3 = TE_GUNSHOT and TE_GUNSHOTQUAD"};
 cvar_t cl_sound_r_exp3 = {CF_CLIENT, "cl_sound_r_exp3", "weapons/r_exp3.wav", "sound to play during TE_EXPLOSION and related effects (empty cvar disables sound)"};
+cvar_t snd_cdautopause = {CF_CLIENT | CF_ARCHIVE, "snd_cdautopause", "1", "pause the CD track while the game is paused"};
+
 cvar_t cl_serverextension_download = {CF_CLIENT, "cl_serverextension_download", "0", "indicates whether the server supports the download command"};
 cvar_t cl_joinbeforedownloadsfinish = {CF_CLIENT | CF_ARCHIVE, "cl_joinbeforedownloadsfinish", "1", "if non-zero the game will begin after the map is loaded before other downloads finish"};
 cvar_t cl_nettimesyncfactor = {CF_CLIENT | CF_ARCHIVE, "cl_nettimesyncfactor", "0", "rate at which client time adapts to match server time, 1 = instantly, 0.125 = slowly, 0 = not at all (only applied in bound modes 0, 1, 2, 3)"};
@@ -197,6 +199,13 @@ static void QW_CL_NextUpload_f(cmd_state_t *cmd);
 //static qbool QW_CL_IsUploading(void);
 static void QW_CL_StopUpload_f(cmd_state_t *cmd);
 
+static inline void CL_SetSignonStage_WithMsg(int signon_stage)
+{
+       cls.signon = signon_stage;
+       dpsnprintf(cl_connect_status, sizeof(cl_connect_status), "Connect: signon stage %i of %i", cls.signon, SIGNONS);
+       Con_DPrint(cl_connect_status);
+}
+
 /*
 ==================
 CL_ParseStartSoundPacket
@@ -331,10 +340,7 @@ void CL_KeepaliveMessage (qbool readmessages)
                if(cls.state != ca_dedicated)
                {
                        if(countdownupdate <= 0) // check if time stepped backwards
-                       {
-                               SCR_UpdateLoadingScreenIfShown();
                                countdownupdate = 2;
-                       }
                }
        }
 
@@ -397,14 +403,14 @@ void CL_ParseEntityLump(char *entdata)
                if (com_token[0] == '}')
                        break; // end of worldspawn
                if (com_token[0] == '_')
-                       strlcpy (key, com_token + 1, sizeof (key));
+                       dp_strlcpy (key, com_token + 1, sizeof (key));
                else
-                       strlcpy (key, com_token, sizeof (key));
+                       dp_strlcpy (key, com_token, sizeof (key));
                while (key[strlen(key)-1] == ' ') // remove trailing spaces
                        key[strlen(key)-1] = 0;
                if (!COM_ParseToken_Simple(&data, false, false, true))
                        return; // error
-               strlcpy (value, com_token, sizeof (value));
+               dp_strlcpy (value, com_token, sizeof (value));
                if (!strcmp("sky", key))
                {
                        loadedsky = true;
@@ -481,9 +487,9 @@ static void CL_SetupWorldModel(void)
        // set up csqc world for collision culling
        if (cl.worldmodel)
        {
-               strlcpy(cl.worldname, cl.worldmodel->name, sizeof(cl.worldname));
+               dp_strlcpy(cl.worldname, cl.worldmodel->name, sizeof(cl.worldname));
                FS_StripExtension(cl.worldname, cl.worldnamenoextension, sizeof(cl.worldnamenoextension));
-               strlcpy(cl.worldbasename, !strncmp(cl.worldnamenoextension, "maps/", 5) ? cl.worldnamenoextension + 5 : cl.worldnamenoextension, sizeof(cl.worldbasename));
+               dp_strlcpy(cl.worldbasename, !strncmp(cl.worldnamenoextension, "maps/", 5) ? cl.worldnamenoextension + 5 : cl.worldnamenoextension, sizeof(cl.worldbasename));
                Cvar_SetQuick(&cl_worldmessage, cl.worldmessage);
                Cvar_SetQuick(&cl_worldname, cl.worldname);
                Cvar_SetQuick(&cl_worldnamenoextension, cl.worldnamenoextension);
@@ -555,7 +561,7 @@ static qbool QW_CL_CheckOrDownloadFile(const char *filename)
        if (!cls.netcon)
                return true;
 
-       strlcpy(cls.qw_downloadname, filename, sizeof(cls.qw_downloadname));
+       dp_strlcpy(cls.qw_downloadname, filename, sizeof(cls.qw_downloadname));
        Con_Printf("Downloading %s\n", filename);
 
        if (!cls.qw_downloadmemory)
@@ -615,7 +621,7 @@ static void QW_CL_RequestNextDownload(void)
                // if we're still in signon stages, request the next one
                if (cls.signon != SIGNONS)
                {
-                       cls.signon = SIGNONS-1;
+                       CL_SetSignonStage_WithMsg(SIGNONS - 1);
                        // we'll go to SIGNONS when the first entity update is received
                        MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd);
                        MSG_WriteString(&cls.netcon->message, va(vabuf, sizeof(vabuf), "begin %i", cl.qw_servercount));
@@ -832,7 +838,7 @@ static void QW_CL_ParseModelList(void)
                        Host_Error("Server sent too many model precaches");
                if (strlen(str) >= MAX_QPATH)
                        Host_Error("Server sent a precache name of %i characters (max %i)", (int)strlen(str), MAX_QPATH - 1);
-               strlcpy(cl.model_name[nummodels], str, sizeof (cl.model_name[nummodels]));
+               dp_strlcpy(cl.model_name[nummodels], str, sizeof (cl.model_name[nummodels]));
        }
 
        n = MSG_ReadByte(&cl_message);
@@ -843,7 +849,7 @@ static void QW_CL_ParseModelList(void)
                return;
        }
 
-       cls.signon = 2;
+       CL_SetSignonStage_WithMsg(2);
        cls.qw_downloadnumber = 0;
        cls.qw_downloadtype = dl_model;
        QW_CL_RequestNextDownload();
@@ -867,7 +873,7 @@ static void QW_CL_ParseSoundList(void)
                        Host_Error("Server sent too many sound precaches");
                if (strlen(str) >= MAX_QPATH)
                        Host_Error("Server sent a precache name of %i characters (max %i)", (int)strlen(str), MAX_QPATH - 1);
-               strlcpy(cl.sound_name[numsounds], str, sizeof (cl.sound_name[numsounds]));
+               dp_strlcpy(cl.sound_name[numsounds], str, sizeof (cl.sound_name[numsounds]));
        }
 
        n = MSG_ReadByte(&cl_message);
@@ -879,7 +885,7 @@ static void QW_CL_ParseSoundList(void)
                return;
        }
 
-       cls.signon = 2;
+       CL_SetSignonStage_WithMsg(2);
        cls.qw_downloadnumber = 0;
        cls.qw_downloadtype = dl_sound;
        QW_CL_RequestNextDownload();
@@ -899,7 +905,7 @@ static void QW_CL_Changing_f(cmd_state_t *cmd)
 
        S_StopAllSounds();
        cl.intermission = 0;
-       cls.signon = 1; // not active anymore, but not disconnected
+       CL_SetSignonStage_WithMsg(1); // not active anymore, but not disconnected
        Con_Printf("\nChanging map...\n");
 }
 
@@ -981,7 +987,7 @@ static void QW_CL_ProcessUserInfo(int slot)
        InfoString_GetValue(cl.scores[slot].qw_userinfo, "team", cl.scores[slot].qw_team, sizeof(cl.scores[slot].qw_team));
        InfoString_GetValue(cl.scores[slot].qw_userinfo, "skin", cl.scores[slot].qw_skin, sizeof(cl.scores[slot].qw_skin));
        if (!cl.scores[slot].qw_skin[0])
-               strlcpy(cl.scores[slot].qw_skin, "base", sizeof(cl.scores[slot].qw_skin));
+               dp_strlcpy(cl.scores[slot].qw_skin, "base", sizeof(cl.scores[slot].qw_skin));
        // TODO: skin cache
 }
 
@@ -997,7 +1003,7 @@ static void QW_CL_UpdateUserInfo(void)
                return;
        }
        cl.scores[slot].qw_userid = MSG_ReadLong(&cl_message);
-       strlcpy(cl.scores[slot].qw_userinfo, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(cl.scores[slot].qw_userinfo));
+       dp_strlcpy(cl.scores[slot].qw_userinfo, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(cl.scores[slot].qw_userinfo));
 
        QW_CL_ProcessUserInfo(slot);
 }
@@ -1008,8 +1014,8 @@ static void QW_CL_SetInfo(void)
        char key[2048];
        char value[2048];
        slot = MSG_ReadByte(&cl_message);
-       strlcpy(key, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(key));
-       strlcpy(value, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(value));
+       dp_strlcpy(key, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(key));
+       dp_strlcpy(value, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(value));
        if (slot >= cl.maxclients)
        {
                Con_Printf("svc_setinfo >= cl.maxclients\n");
@@ -1025,8 +1031,8 @@ static void QW_CL_ServerInfo(void)
        char key[2048];
        char value[2048];
        char temp[32];
-       strlcpy(key, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(key));
-       strlcpy(value, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(value));
+       dp_strlcpy(key, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(key));
+       dp_strlcpy(value, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(value));
        Con_DPrintf("SERVERINFO: %s=%s\n", key, value);
        InfoString_SetValue(cl.qw_serverinfo, sizeof(cl.qw_serverinfo), key, value);
        InfoString_GetValue(cl.qw_serverinfo, "teamplay", temp, sizeof(temp));
@@ -1061,9 +1067,10 @@ static void CL_UpdateItemsAndWeapon(void)
        // check for important changes
 
        // set flash times
+       // UBSan: unsigned literals because left shifting by 31 causes signed overflow, although it works as expected on x86.
        if (cl.olditems != cl.stats[STAT_ITEMS])
                for (j = 0;j < 32;j++)
-                       if ((cl.stats[STAT_ITEMS] & (1<<j)) && !(cl.olditems & (1<<j)))
+                       if ((cl.stats[STAT_ITEMS] & (1u<<j)) && !(cl.olditems & (1u<<j)))
                                cl.item_gettime[j] = cl.time;
        cl.olditems = cl.stats[STAT_ITEMS];
 
@@ -1524,7 +1531,7 @@ static void CL_DownloadBegin_f(cmd_state_t *cmd)
        CL_StopDownload(0, 0);
 
        // we're really beginning a download now, so initialize stuff
-       strlcpy(cls.qw_downloadname, Cmd_Argv(cmd, 2), sizeof(cls.qw_downloadname));
+       dp_strlcpy(cls.qw_downloadname, Cmd_Argv(cmd, 2), sizeof(cls.qw_downloadname));
        cls.qw_downloadmemorymaxsize = size;
        cls.qw_downloadmemory = (unsigned char *) Mem_Alloc(cls.permanentmempool, cls.qw_downloadmemorymaxsize);
        cls.qw_downloadnumber++;
@@ -1603,8 +1610,10 @@ CL_SignonReply
 An svc_signonnum has been received, perform a client side setup
 =====================
 */
-static void CL_SignonReply (void)
+static void CL_SignonReply(int signon_stage)
 {
+       CL_SetSignonStage_WithMsg(signon_stage);
+
        Con_DPrintf("CL_SignonReply: %i\n", cls.signon);
 
        switch (cls.signon)
@@ -1616,16 +1625,13 @@ static void CL_SignonReply (void)
                        // (so that the server can see the player name while downloading)
                        CL_SendPlayerInfo();
 
-                       // execute cl_begindownloads next frame
-                       // (after any commands added by svc_stufftext have been executed)
-                       // when done with downloads the "prespawn" will be sent
-                       Cbuf_AddText(cmd_local, "\ncl_begindownloads\n");
-
                        //MSG_WriteByte (&cls.netcon->message, clc_stringcmd);
                        //MSG_WriteString (&cls.netcon->message, "prespawn");
                }
-               else // playing a demo...  make sure loading occurs as soon as possible
-                       CL_BeginDownloads(false);
+               // execute cl_begindownloads next frame
+               // (after any commands added by svc_stufftext have been executed)
+               // when done with downloads the "prespawn" will be sent
+               Cbuf_AddText(cmd_local, "\ncl_begindownloads\n");
                break;
 
        case 2:
@@ -1688,8 +1694,11 @@ static void CL_ParseServerInfo (void)
        {
                SCR_BeginLoadingPlaque(false);
                S_StopAllSounds();
+               // prevent dlcache assets from the previous map from interfering with this one
+               FS_UnloadPacks_dlcache();
                // free q3 shaders so that any newly downloaded shaders will be active
-               Mod_FreeQ3Shaders();
+               // bones_was_here: we free the q3 shaders later in CL_SignonReply
+               //Mod_FreeQ3Shaders();
        }
 
        // check memory integrity
@@ -1727,7 +1736,7 @@ static void CL_ParseServerInfo (void)
 
                str = MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
                Con_Printf("server gamedir is %s\n", str);
-               strlcpy(gamedir[0], str, sizeof(gamedir[0]));
+               dp_strlcpy(gamedir[0], str, sizeof(gamedir[0]));
 
                // change gamedir if needed
                if (!FS_ChangeGameDirs(1, gamedir, true, false))
@@ -1745,7 +1754,7 @@ static void CL_ParseServerInfo (void)
 
                // get the full level name
                str = MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
-               strlcpy (cl.worldmessage, str, sizeof(cl.worldmessage));
+               dp_strlcpy (cl.worldmessage, str, sizeof(cl.worldmessage));
 
                // get the movevars that are defined in the qw protocol
                cl.movevars_gravity            = MSG_ReadFloat(&cl_message);
@@ -1785,16 +1794,16 @@ static void CL_ParseServerInfo (void)
                cl.loadfinished = false;
 
                cls.state = ca_connected;
-               cls.signon = 1;
+               CL_SetSignonStage_WithMsg(1);
 
                // note: on QW protocol we can't set up the gameworld until after
                // downloads finish...
                // (we don't even know the name of the map yet)
                // this also means cl_autodemo does not work on QW protocol...
 
-               strlcpy(cl.worldname, "", sizeof(cl.worldname));
-               strlcpy(cl.worldnamenoextension, "", sizeof(cl.worldnamenoextension));
-               strlcpy(cl.worldbasename, "qw", sizeof(cl.worldbasename));
+               dp_strlcpy(cl.worldname, "", sizeof(cl.worldname));
+               dp_strlcpy(cl.worldnamenoextension, "", sizeof(cl.worldnamenoextension));
+               dp_strlcpy(cl.worldbasename, "qw", sizeof(cl.worldbasename));
                Cvar_SetQuick(&cl_worldname, cl.worldname);
                Cvar_SetQuick(&cl_worldnamenoextension, cl.worldnamenoextension);
                Cvar_SetQuick(&cl_worldbasename, cl.worldbasename);
@@ -1822,7 +1831,7 @@ static void CL_ParseServerInfo (void)
 
        // parse signon message
                str = MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
-               strlcpy (cl.worldmessage, str, sizeof(cl.worldmessage));
+               dp_strlcpy (cl.worldmessage, str, sizeof(cl.worldmessage));
 
        // seperate the printfs so the server message can have a color
                if (cls.protocol != PROTOCOL_NEHAHRAMOVIE) // no messages when playing the Nehahra movie
@@ -1841,7 +1850,7 @@ static void CL_ParseServerInfo (void)
                                Host_Error ("Server sent too many model precaches");
                        if (strlen(str) >= MAX_QPATH)
                                Host_Error ("Server sent a precache name of %i characters (max %i)", (int)strlen(str), MAX_QPATH - 1);
-                       strlcpy (cl.model_name[nummodels], str, sizeof (cl.model_name[nummodels]));
+                       dp_strlcpy (cl.model_name[nummodels], str, sizeof (cl.model_name[nummodels]));
                }
                // parse sound precache list
                for (numsounds=1 ; ; numsounds++)
@@ -1853,13 +1862,13 @@ static void CL_ParseServerInfo (void)
                                Host_Error("Server sent too many sound precaches");
                        if (strlen(str) >= MAX_QPATH)
                                Host_Error("Server sent a precache name of %i characters (max %i)", (int)strlen(str), MAX_QPATH - 1);
-                       strlcpy (cl.sound_name[numsounds], str, sizeof (cl.sound_name[numsounds]));
+                       dp_strlcpy (cl.sound_name[numsounds], str, sizeof (cl.sound_name[numsounds]));
                }
 
                // set the base name for level-specific things...  this gets updated again by CL_SetupWorldModel later
-               strlcpy(cl.worldname, cl.model_name[1], sizeof(cl.worldname));
+               dp_strlcpy(cl.worldname, cl.model_name[1], sizeof(cl.worldname));
                FS_StripExtension(cl.worldname, cl.worldnamenoextension, sizeof(cl.worldnamenoextension));
-               strlcpy(cl.worldbasename, !strncmp(cl.worldnamenoextension, "maps/", 5) ? cl.worldnamenoextension + 5 : cl.worldnamenoextension, sizeof(cl.worldbasename));
+               dp_strlcpy(cl.worldbasename, !strncmp(cl.worldnamenoextension, "maps/", 5) ? cl.worldnamenoextension + 5 : cl.worldnamenoextension, sizeof(cl.worldbasename));
                Cvar_SetQuick(&cl_worldmessage, cl.worldmessage);
                Cvar_SetQuick(&cl_worldname, cl.worldname);
                Cvar_SetQuick(&cl_worldnamenoextension, cl.worldnamenoextension);
@@ -1941,7 +1950,7 @@ static void CL_ParseServerInfo (void)
                                cls.forcetrack = -1;
                                FS_Printf (cls.demofile, "%i\n", cls.forcetrack);
                                cls.demorecording = true;
-                               strlcpy(cls.demoname, demofile, sizeof(cls.demoname));
+                               dp_strlcpy(cls.demoname, demofile, sizeof(cls.demoname));
                                cls.demo_lastcsprogssize = -1;
                                cls.demo_lastcsprogscrc = -1;
                        }
@@ -3024,10 +3033,10 @@ static void CL_IPLog_Add(const char *address, const char *name, qbool checkexist
        sz_name = strlen(name) + 1;
        cl_iplog_items[cl_iplog_numitems].address = (char *) Mem_Alloc(cls.permanentmempool, sz_address);
        cl_iplog_items[cl_iplog_numitems].name = (char *) Mem_Alloc(cls.permanentmempool, sz_name);
-       strlcpy(cl_iplog_items[cl_iplog_numitems].address, address, sz_address);
+       dp_strlcpy(cl_iplog_items[cl_iplog_numitems].address, address, sz_address);
        // TODO: maybe it would be better to strip weird characters from name when
        // copying it here rather than using a straight strcpy?
-       strlcpy(cl_iplog_items[cl_iplog_numitems].name, name, sz_name);
+       dp_strlcpy(cl_iplog_items[cl_iplog_numitems].name, name, sz_name);
        cl_iplog_numitems++;
        if (addtofile)
        {
@@ -3279,9 +3288,10 @@ extern cvar_t host_timescale;
 extern cvar_t cl_lerpexcess;
 static void CL_NetworkTimeReceived(double newtime)
 {
+       cl.opt_inputs_since_update = 0;
        cl.mtime[1] = cl.mtime[0];
        cl.mtime[0] = newtime;
-       if (cl_nolerp.integer || cls.timedemo || cl.mtime[1] == cl.mtime[0] || cls.signon < SIGNONS)
+       if (cls.timedemo || cl.mtime[1] == cl.mtime[0] || cls.signon < SIGNONS)
                cl.time = cl.mtime[1] = newtime;
        else if (cls.demoplayback)
        {
@@ -3304,27 +3314,32 @@ static void CL_NetworkTimeReceived(double newtime)
                                Con_DPrintf("--- cl.time > cl.mtime[0] (%f > %f ... %f)\n", cl.time, cl.mtime[1], cl.mtime[0]);
                }
 
-               if (cl_nettimesyncboundmode.integer < 4)
-               {
-                       // doesn't make sense for modes > 3
-                       cl.time += (cl.mtime[1] - cl.time) * bound(0, cl_nettimesyncfactor.value, 1);
-                       timehigh = cl.mtime[1] + (cl.mtime[0] - cl.mtime[1]) * cl_nettimesyncboundtolerance.value;
-               }
-
+selectmode:
                switch (cl_nettimesyncboundmode.integer)
                {
+               default:
+                       Cvar_SetQuick(&cl_nettimesyncboundmode, cl_nettimesyncboundmode.defstring);
+                       goto selectmode;
                case 1:
-                       cl.time = bound(cl.mtime[1], cl.time, cl.mtime[0]);
-                       break;
-
                case 2:
-                       if (cl.time < cl.mtime[1] || cl.time > timehigh)
-                               cl.time = cl.mtime[1];
-                       break;
-
                case 3:
-                       if ((cl.time < cl.mtime[1] && cl.oldtime < cl.mtime[1]) || (cl.time > timehigh && cl.oldtime > timehigh))
-                               cl.time = cl.mtime[1];
+                       // doesn't make sense for modes > 3
+                       cl.time += (cl.mtime[1] - cl.time) * bound(0, cl_nettimesyncfactor.value, 1);
+                       timehigh = cl.mtime[1] + (cl.mtime[0] - cl.mtime[1]) * cl_nettimesyncboundtolerance.value;
+                       switch (cl_nettimesyncboundmode.integer)
+                       {
+                       case 1:
+                               cl.time = bound(cl.mtime[1], cl.time, cl.mtime[0]);
+                               break;
+                       case 2:
+                               if (cl.time < cl.mtime[1] || cl.time > timehigh)
+                                       cl.time = cl.mtime[1];
+                               break;
+                       case 3:
+                               if ((cl.time < cl.mtime[1] && cl.oldtime < cl.mtime[1]) || (cl.time > timehigh && cl.oldtime > timehigh))
+                                       cl.time = cl.mtime[1];
+                               break;
+                       }
                        break;
 
                case 4:
@@ -3430,6 +3445,7 @@ void CL_ParseServerMessage(void)
        qbool   qwplayerupdatereceived;
        qbool   strip_pqc;
        char vabuf[1024];
+       size_t cl_readstring_len;
 
        // LadyHavoc: moved demo message writing from before the packet parse to
        // after the packet parse so that CL_Stop_f can be called by cl_autodemo
@@ -3503,7 +3519,7 @@ void CL_ParseServerMessage(void)
                                {
                                        char description[32*64], logtemp[64];
                                        int count;
-                                       strlcpy(description, "packet dump: ", sizeof(description));
+                                       dp_strlcpy(description, "packet dump: ", sizeof(description));
                                        i = cmdcount - 32;
                                        if (i < 0)
                                                i = 0;
@@ -3512,7 +3528,7 @@ void CL_ParseServerMessage(void)
                                        while(count > 0)
                                        {
                                                dpsnprintf(logtemp, sizeof(logtemp), "%3i:%s ", cmdlog[i], cmdlogname[i]);
-                                               strlcat(description, logtemp, sizeof(description));
+                                               dp_strlcat(description, logtemp, sizeof(description));
                                                count--;
                                                i++;
                                                i &= 31;
@@ -3536,22 +3552,28 @@ void CL_ParseServerMessage(void)
 
                        case qw_svc_print:
                                i = MSG_ReadByte(&cl_message);
-                               temp = MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
+                               cl_readstring_len = MSG_ReadString_len(&cl_message, cl_readstring, sizeof(cl_readstring));
+                               temp = cl_readstring;
                                if (CL_ExaminePrintString(temp)) // look for anything interesting like player IP addresses or ping reports
                                {
                                        if (i == 3) // chat
-                                               CSQC_AddPrintText(va(vabuf, sizeof(vabuf), "\1%s", temp));      //[515]: csqc
+                                       {
+                                               cl_readstring_len = dpsnprintf(vabuf, sizeof(vabuf), "\1%s", temp);
+                                               CSQC_AddPrintText(vabuf, cl_readstring_len);    //[515]: csqc
+                                       }
                                        else
-                                               CSQC_AddPrintText(temp);
+                                               CSQC_AddPrintText(temp, cl_readstring_len);
                                }
                                break;
 
                        case qw_svc_centerprint:
-                               CL_VM_Parse_CenterPrint(MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));     //[515]: csqc
+                               cl_readstring_len = MSG_ReadString_len(&cl_message, cl_readstring, sizeof(cl_readstring));
+                               CL_VM_Parse_CenterPrint(cl_readstring, cl_readstring_len);      //[515]: csqc
                                break;
 
                        case qw_svc_stufftext:
-                               CL_VM_Parse_StuffCmd(MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));        //[515]: csqc
+                               cl_readstring_len = MSG_ReadString_len(&cl_message, cl_readstring, sizeof(cl_readstring));
+                               CL_VM_Parse_StuffCmd(cl_readstring, cl_readstring_len); //[515]: csqc
                                break;
 
                        case qw_svc_damage:
@@ -3584,7 +3606,7 @@ void CL_ParseServerMessage(void)
                                        Con_Printf ("svc_lightstyle >= MAX_LIGHTSTYLES");
                                        break;
                                }
-                               strlcpy (cl.lightstyle[i].map,  MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof (cl.lightstyle[i].map));
+                               dp_strlcpy (cl.lightstyle[i].map,  MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof (cl.lightstyle[i].map));
                                cl.lightstyle[i].map[MAX_STYLESTRING - 1] = 0;
                                cl.lightstyle[i].length = (int)strlen(cl.lightstyle[i].map);
                                break;
@@ -3694,7 +3716,7 @@ void CL_ParseServerMessage(void)
                                break;
 
                        case qw_svc_sellscreen:
-                               Cmd_ExecuteString(cmd_local, "help", src_local, true);
+                               Cmd_ExecuteString(cmd_local, "help", 4, src_local, true);
                                break;
 
                        case qw_svc_smallkick:
@@ -3765,20 +3787,14 @@ void CL_ParseServerMessage(void)
                                EntityFrameQW_CL_ReadFrame(false);
                                // first update is the final signon stage
                                if (cls.signon == SIGNONS - 1)
-                               {
-                                       cls.signon = SIGNONS;
-                                       CL_SignonReply ();
-                               }
+                                       CL_SignonReply(SIGNONS);
                                break;
 
                        case qw_svc_deltapacketentities:
                                EntityFrameQW_CL_ReadFrame(true);
                                // first update is the final signon stage
                                if (cls.signon == SIGNONS - 1)
-                               {
-                                       cls.signon = SIGNONS;
-                                       CL_SignonReply ();
-                               }
+                                       CL_SignonReply(SIGNONS);
                                break;
 
                        case qw_svc_maxspeed:
@@ -3793,9 +3809,9 @@ void CL_ParseServerMessage(void)
 
                        case qw_svc_setpause:
                                cl.paused = MSG_ReadByte(&cl_message) != 0;
-                               if (cl.paused)
+                               if (cl.paused && snd_cdautopause.integer)
                                        CDAudio_Pause ();
-                               else
+                               else if (bgmvolume.value > 0.0f)
                                        CDAudio_Resume ();
                                S_PauseGameSounds (cl.paused);
                                break;
@@ -3838,11 +3854,8 @@ void CL_ParseServerMessage(void)
                                cmdlogname[cmdindex] = temp;
                                SHOWNET("fast update");
                                if (cls.signon == SIGNONS - 1)
-                               {
                                        // first update is the final signon stage
-                                       cls.signon = SIGNONS;
-                                       CL_SignonReply ();
-                               }
+                                       CL_SignonReply(SIGNONS);
                                EntityFrameQuake_ReadEntity (cmd&127);
                                continue;
                        }
@@ -3863,7 +3876,7 @@ void CL_ParseServerMessage(void)
                                {
                                        char description[32*64], tempdesc[64];
                                        int count;
-                                       strlcpy (description, "packet dump: ", sizeof(description));
+                                       dp_strlcpy (description, "packet dump: ", sizeof(description));
                                        i = cmdcount - 32;
                                        if (i < 0)
                                                i = 0;
@@ -3872,7 +3885,7 @@ void CL_ParseServerMessage(void)
                                        while(count > 0)
                                        {
                                                dpsnprintf (tempdesc, sizeof (tempdesc), "%3i:%s ", cmdlog[i], cmdlogname[i]);
-                                               strlcat (description, tempdesc, sizeof (description));
+                                               dp_strlcat (description, tempdesc, sizeof (description));
                                                count--;
                                                i++;
                                                i &= 31;
@@ -3915,17 +3928,20 @@ void CL_ParseServerMessage(void)
                                break;
 
                        case svc_print:
-                               temp = MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
+                               cl_readstring_len = MSG_ReadString_len(&cl_message, cl_readstring, sizeof(cl_readstring));
+                               temp = cl_readstring;
                                if (CL_ExaminePrintString(temp)) // look for anything interesting like player IP addresses or ping reports
-                                       CSQC_AddPrintText(temp);        //[515]: csqc
+                                       CSQC_AddPrintText(temp, cl_readstring_len);     //[515]: csqc
                                break;
 
                        case svc_centerprint:
-                               CL_VM_Parse_CenterPrint(MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));     //[515]: csqc
+                               cl_readstring_len = MSG_ReadString_len(&cl_message, cl_readstring, sizeof(cl_readstring));
+                               CL_VM_Parse_CenterPrint(cl_readstring, cl_readstring_len);      //[515]: csqc
                                break;
 
                        case svc_stufftext:
-                               temp = MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
+                               cl_readstring_len = MSG_ReadString_len(&cl_message, cl_readstring, sizeof(cl_readstring));
+                               temp = cl_readstring;
                                /* if(utf8_enable.integer)
                                {
                                        strip_pqc = true;
@@ -3956,11 +3972,15 @@ void CL_ParseServerMessage(void)
                                        if(*temp == 0x01)
                                        {
                                                ++temp;
+                                               --cl_readstring_len;
                                                while(*temp >= 0x01 && *temp <= 0x1F)
+                                               {
                                                        ++temp;
+                                                       --cl_readstring_len;
+                                               }
                                        }
                                }
-                               CL_VM_Parse_StuffCmd(temp);     //[515]: csqc
+                               CL_VM_Parse_StuffCmd(temp, cl_readstring_len);  //[515]: csqc
                                break;
 
                        case svc_damage:
@@ -4005,7 +4025,7 @@ void CL_ParseServerMessage(void)
                                        Con_Printf ("svc_lightstyle >= MAX_LIGHTSTYLES");
                                        break;
                                }
-                               strlcpy (cl.lightstyle[i].map,  MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof (cl.lightstyle[i].map));
+                               dp_strlcpy (cl.lightstyle[i].map,  MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof (cl.lightstyle[i].map));
                                cl.lightstyle[i].map[MAX_STYLESTRING - 1] = 0;
                                cl.lightstyle[i].length = (int)strlen(cl.lightstyle[i].map);
                                break;
@@ -4062,7 +4082,7 @@ void CL_ParseServerMessage(void)
                                i = MSG_ReadByte(&cl_message);
                                if (i >= cl.maxclients)
                                        Host_Error ("CL_ParseServerMessage: svc_updatename >= cl.maxclients");
-                               strlcpy (cl.scores[i].name, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof (cl.scores[i].name));
+                               dp_strlcpy (cl.scores[i].name, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof (cl.scores[i].name));
                                break;
 
                        case svc_updatefrags:
@@ -4120,9 +4140,9 @@ void CL_ParseServerMessage(void)
 
                        case svc_setpause:
                                cl.paused = MSG_ReadByte(&cl_message) != 0;
-                               if (cl.paused)
+                               if (cl.paused && snd_cdautopause.integer)
                                        CDAudio_Pause ();
-                               else
+                               else if (bgmvolume.value > 0.0f)
                                        CDAudio_Resume ();
                                S_PauseGameSounds (cl.paused);
                                break;
@@ -4133,8 +4153,7 @@ void CL_ParseServerMessage(void)
                                // reconnect somehow, so allow signon 1 even if at signon 1
                                if (i <= cls.signon && i != 1)
                                        Host_Error ("Received signon %i when at %i", i, cls.signon);
-                               cls.signon = i;
-                               CL_SignonReply ();
+                               CL_SignonReply(i);
                                break;
 
                        case svc_killedmonster:
@@ -4200,7 +4219,7 @@ void CL_ParseServerMessage(void)
                                break;
 
                        case svc_sellscreen:
-                               Cmd_ExecuteString(cmd_local, "help", src_local, true);
+                               Cmd_ExecuteString(cmd_local, "help", 4, src_local, true);
                                break;
                        case svc_hidelmp:
                                if (gamemode == GAME_TENEBRAE)
@@ -4238,11 +4257,8 @@ void CL_ParseServerMessage(void)
                                break;
                        case svc_entities:
                                if (cls.signon == SIGNONS - 1)
-                               {
                                        // first update is the final signon stage
-                                       cls.signon = SIGNONS;
-                                       CL_SignonReply ();
-                               }
+                                       CL_SignonReply(SIGNONS);
                                if (cls.protocol == PROTOCOL_DARKPLACES1 || cls.protocol == PROTOCOL_DARKPLACES2 || cls.protocol == PROTOCOL_DARKPLACES3)
                                        EntityFrame_CL_ReadFrame();
                                else if (cls.protocol == PROTOCOL_DARKPLACES4)
@@ -4324,6 +4340,7 @@ void CL_Parse_Init(void)
        Cvar_RegisterVariable(&cl_sound_ric3);
        Cvar_RegisterVariable(&cl_sound_ric_gunshot);
        Cvar_RegisterVariable(&cl_sound_r_exp3);
+       Cvar_RegisterVariable(&snd_cdautopause);
 
        Cvar_RegisterVariable(&cl_joinbeforedownloadsfinish);
 
index 64fe93b8ce10c176dccba3eb10cbb553a51d1bc3..bb8840da27ca60a7855f6cd49d157b4617a41ba1 100644 (file)
@@ -192,10 +192,11 @@ static const int tex_bulletdecal[8] = {8, 9, 10, 11, 12, 13, 14, 15};
 static const int tex_blooddecal[8] = {16, 17, 18, 19, 20, 21, 22, 23};
 static const int tex_bloodparticle[8] = {24, 25, 26, 27, 28, 29, 30, 31};
 static const int tex_rainsplash = 32;
-static const int tex_particle = 63;
+static const int tex_square = 33;
+static const int tex_beam = 60;
 static const int tex_bubble = 62;
 static const int tex_raindrop = 61;
-static const int tex_beam = 60;
+static const int tex_particle = 63;
 
 particleeffectinfo_t baselineparticleeffectinfo =
 {
@@ -279,7 +280,7 @@ cvar_t cl_particles = {CF_CLIENT | CF_ARCHIVE, "cl_particles", "1", "enables par
 cvar_t cl_particles_quality = {CF_CLIENT | CF_ARCHIVE, "cl_particles_quality", "1", "multiplies number of particles"};
 cvar_t cl_particles_alpha = {CF_CLIENT | CF_ARCHIVE, "cl_particles_alpha", "1", "multiplies opacity of particles"};
 cvar_t cl_particles_size = {CF_CLIENT | CF_ARCHIVE, "cl_particles_size", "1", "multiplies particle size"};
-cvar_t cl_particles_quake = {CF_CLIENT | CF_ARCHIVE, "cl_particles_quake", "0", "makes particle effects look mostly like the ones in Quake"};
+cvar_t cl_particles_quake = {CF_CLIENT | CF_ARCHIVE, "cl_particles_quake", "0", "0: Fancy particles; 1: Disc particles like GLQuake; 2: Square particles like software-rendered Quake"};
 cvar_t cl_particles_blood = {CF_CLIENT | CF_ARCHIVE, "cl_particles_blood", "1", "enables blood effects"};
 cvar_t cl_particles_blood_alpha = {CF_CLIENT | CF_ARCHIVE, "cl_particles_blood_alpha", "1", "opacity of blood, does not affect decals"};
 cvar_t cl_particles_blood_decal_alpha = {CF_CLIENT | CF_ARCHIVE, "cl_particles_blood_decal_alpha", "1", "opacity of blood decal"};
@@ -332,7 +333,7 @@ static void CL_Particles_ParseEffectInfo(const char *textstart, const char *text
                                break;
                        if (argc < 16)
                        {
-                               strlcpy(argv[argc], com_token, sizeof(argv[argc]));
+                               dp_strlcpy(argv[argc], com_token, sizeof(argv[argc]));
                                argc++;
                        }
                }
@@ -362,7 +363,7 @@ static void CL_Particles_ParseEffectInfo(const char *textstart, const char *text
                                }
                                else
                                {
-                                       strlcpy(particleeffectname[effectnameindex], argv[1], sizeof(particleeffectname[effectnameindex]));
+                                       dp_strlcpy(particleeffectname[effectnameindex], argv[1], sizeof(particleeffectname[effectnameindex]));
                                        break;
                                }
                        }
@@ -545,15 +546,15 @@ static void CL_Particles_LoadEffectInfo(const char *customfile)
        memset(particleeffectinfo, 0, sizeof(particleeffectinfo));
        memset(particleeffectname, 0, sizeof(particleeffectname));
        for (i = 0;i < EFFECT_TOTAL;i++)
-               strlcpy(particleeffectname[i], standardeffectnames[i], sizeof(particleeffectname[i]));
+               dp_strlcpy(particleeffectname[i], standardeffectnames[i], sizeof(particleeffectname[i]));
        for (filepass = 0;;filepass++)
        {
                if (filepass == 0)
                {
                        if (customfile)
-                               strlcpy(filename, customfile, sizeof(filename));
+                               dp_strlcpy(filename, customfile, sizeof(filename));
                        else
-                               strlcpy(filename, "effectinfo.txt", sizeof(filename));
+                               dp_strlcpy(filename, "effectinfo.txt", sizeof(filename));
                }
                else if (filepass == 1)
                {
@@ -629,28 +630,71 @@ void CL_Particles_Shutdown (void)
 void CL_SpawnDecalParticleForSurface(int hitent, const vec3_t org, const vec3_t normal, int color1, int color2, int texnum, float size, float alpha);
 void CL_SpawnDecalParticleForPoint(const vec3_t org, float maxdist, float size, float alpha, int texnum, int color1, int color2);
 
-// list of all 26 parameters:
-// ptype - any of the pt_ enum values (pt_static, pt_blood, etc), see ptype_t near the top of this file
-// pcolor1,pcolor2 - minimum and maximum ranges of color, randomly interpolated to decide particle color
-// ptex - any of the tex_ values such as tex_smoke[rand()&7] or tex_particle
-// psize - size of particle (or thickness for PARTICLE_SPARK and PARTICLE_*BEAM)
-// palpha - opacity of particle as 0-255 (can be more than 255)
-// palphafade - rate of fade per second (so 256 would mean a 256 alpha particle would fade to nothing in 1 second)
-// ptime - how long the particle can live (note it is also removed if alpha drops to nothing)
-// pgravity - how much effect gravity has on the particle (0-1)
-// pbounce - how much bounce the particle has when it hits a surface (0-1), -1 makes a blood splat when it hits a surface, 0 does not even check for collisions
-// px,py,pz - starting origin of particle
-// pvx,pvy,pvz - starting velocity of particle
-// pfriction - how much the particle slows down per second (0-1 typically, can slowdown faster than 1)
-// blendmode - one of the PBLEND_ values
-// orientation - one of the PARTICLE_ values
-// staincolor1, staincolor2: minimum and maximum ranges of stain color, randomly interpolated to decide stain color (-1 to use none)
-// staintex: any of the tex_ values such as tex_smoke[rand()&7] or tex_particle (-1 to use none)
-// stainalpha: opacity of the stain as factor for alpha
-// stainsize: size of the stain as factor for palpha
-// angle: base rotation of the particle geometry around its center normal
-// spin: rotation speed of the particle geometry around its center normal
-particle_t *CL_NewParticle(const vec3_t sortorigin, unsigned short ptypeindex, int pcolor1, int pcolor2, int ptex, float psize, float psizeincrease, float palpha, float palphafade, float pgravity, float pbounce, float px, float py, float pz, float pvx, float pvy, float pvz, float pairfriction, float pliquidfriction, float originjitter, float velocityjitter, qbool pqualityreduction, float lifetime, float stretch, pblend_t blendmode, porientation_t orientation, int staincolor1, int staincolor2, int staintex, float stainalpha, float stainsize, float angle, float spin, float tint[4])
+
+
+/**
+ * @brief      Creates a new particle and returns a pointer to it
+ *
+ * @param[in]  sortorigin         ?
+ * @param[in]  ptypeindex         Any of the pt_ enum values (pt_static, pt_blood, etc), see ptype_t near the top of this file
+ * @param[in]  pcolor1,pcolor2    Minimum and maximum range of color, randomly interpolated with pcolor2 to decide particle color
+ * @param[in]  ptex               Any of the tex_ values such as tex_smoke[rand()&7] or tex_particle
+ * @param[in]  psize              Size of particle (or thickness for PARTICLE_SPARK and PARTICLE_*BEAM)
+ * @param[in]  psizeincrease      ?
+ * @param[in]  palpha             Opacity of particle as 0-255 (can be more than 255)
+ * @param[in]  palphafade         Rate of fade per second (so 256 would mean a 256 alpha particle would fade to nothing in 1 second)
+ * @param[in]  pgravity           How much effect gravity has on the particle (0-1)
+ * @param[in]  pbounce            How much bounce the particle has when it hits a surface (0-1), -1 makes a blood splat when it hits a surface, 0 does not even check for collisions
+ * @param[in]  px,py,pz           Starting origin of particle
+ * @param[in]  pvx,pvy,pvz        Starting velocity of particle
+ * @param[in]  pairfriction       How much the particle slows down, in air, per second (0-1 typically, can slowdown faster than 1)
+ * @param[in]  pliquidfriction    How much the particle slows down, in liquids, per second (0-1 typically, can slowdown faster than 1)
+ * @param[in]  originjitter       ?
+ * @param[in]  velocityjitter     ?
+ * @param[in]  pqualityreduction  ?
+ * @param[in]  lifetime           How long the particle can live (note it is also removed if alpha drops to nothing)
+ * @param[in]  stretch            ?
+ * @param[in]  blendmode          One of the PBLEND_ values
+ * @param[in]  orientation        One of the PARTICLE_ values
+ * @param[in]  staincolor1        Minimum and maximum ranges of stain color, randomly interpolated to decide stain color (-1 to use none)
+ * @param[in]  staincolor2        Minimum and maximum ranges of stain color, randomly interpolated to decide stain color (-1 to use none)
+ * @param[in]  staintex           Any of the tex_ values such as tex_smoke[rand()&7] or tex_particle
+ * @param[in]  angle              Base rotation of the particle geometry around its center normal
+ * @param[in]  spin               Rotation speed of the particle geometry around its center normal
+ * @param[in]  tint               The tint
+ *
+ * @return     Pointer to the new particle
+ */
+particle_t *CL_NewParticle(
+       const vec3_t sortorigin,
+       unsigned short ptypeindex,
+       int pcolor1, int pcolor2,
+       int ptex,
+       float psize,
+       float psizeincrease,
+       float palpha,
+       float palphafade,
+       float pgravity,
+       float pbounce,
+       float px, float py, float pz,
+       float pvx, float pvy, float pvz,
+       float pairfriction,
+       float pliquidfriction,
+       float originjitter,
+       float velocityjitter,
+       qbool pqualityreduction,
+       float lifetime,
+       float stretch,
+       pblend_t blendmode,
+       porientation_t orientation,
+       int staincolor1,
+       int staincolor2,
+       int staintex,
+       float stainalpha,
+       float stainsize,
+       float angle,
+       float spin,
+       float tint[4])
 {
        int l1, l2, r, g, b;
        particle_t *part;
@@ -802,6 +846,89 @@ particle_t *CL_NewParticle(const vec3_t sortorigin, unsigned short ptypeindex, i
        return part;
 }
 
+
+
+/**
+ * @brief      Creates a simple particle, a square like Quake, or a disc like GLQuake
+ *
+ * @param[in]  origin                                                 ?
+ * @param[in]  color_1,color_2                                        Minimum and maximum range of color, randomly interpolated with pcolor2 to decide particle color
+ * @param[in]  gravity                                                How much effect gravity has on the particle (0-1)
+ * @param[in]  offset_x,offset_y,offset_z                             Starting origin of particle
+ * @param[in]  velocity_offset_x,velocity_offset_y,velocity_offset_z  Starting velocity of particle
+ * @param[in]  air_friction                                           How much the particle slows down, in air, per second (0-1 typically, can slowdown faster than 1)
+ * @param[in]  liquid_friction                                        How much the particle slows down, in liquids, per second (0-1 typically, can slowdown faster than 1)
+ * @param[in]  origin_jitter                                          ?
+ * @param[in]  velocity_jitter                                        ?
+ * @param[in]  lifetime                                               How long the particle can live (note it is also removed if alpha drops to nothing)
+ *
+ * @return     Pointer to the new particle
+ */
+particle_t *CL_NewQuakeParticle(
+       const vec3_t origin,
+       const int color_1,
+       const int color_2,
+       const float gravity,
+       const float offset_x,
+       const float offset_y,
+       const float offset_z,
+       const float velocity_offset_x,
+       const float velocity_offset_y,
+       const float velocity_offset_z,
+       const float air_friction,
+       const float liquid_friction,
+       const float origin_jitter,
+       const float velocity_jitter,
+       const float lifetime)
+{
+       int texture;
+
+       // Set the particle texture based on the value of cl_particles_quake; defaulting to the GLQuake disc
+       if (cl_particles_quake.integer == 2)
+               texture = tex_square;
+       else
+               texture = tex_particle;
+
+       return CL_NewParticle(
+               origin,
+               pt_alphastatic,      // type
+               color_1,
+               color_2,
+               texture,
+               0.8f,                // size
+               0,                   // size increase
+               255,                 // alpha
+               0,                   // alpha fade
+               gravity,
+               0,                   // bounce
+               offset_x,
+               offset_y,
+               offset_z,
+               velocity_offset_x,
+               velocity_offset_y,
+               velocity_offset_z,
+               air_friction,
+               liquid_friction,
+               origin_jitter,
+               velocity_jitter,
+               true,                // quality reduction
+               lifetime,
+               1,                   // stretch
+               PBLEND_ALPHA,        // blend mode
+               PARTICLE_BILLBOARD,  // orientation
+               -1,                  // stain color 1
+               -1,                  // stain color 2
+               -1,                  // stain texture
+               1,                   // stain alpha
+               1,                   // stain size
+               0,                   // angle
+               0,                   // spin
+               NULL                 // tint
+       );
+}
+
+
+
 static void CL_ImmediateBloodStain(particle_t *part)
 {
        vec3_t v;
@@ -914,7 +1041,23 @@ static void CL_ParticleEffect_Fallback(int effectnameindex, float count, const v
                                for (;count > 0;count--)
                                {
                                        int k = particlepalette[(palettecolor & ~7) + (rand()&7)];
-                                       CL_NewParticle(center, pt_alphastatic, k, k, tex_particle, 1.5, 0, 255, 0, 0.15, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 0, 0, 8, 3, true, lhrandom(0.1, 0.4), 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1, 0, 0, NULL);
+                                       CL_NewQuakeParticle(
+                                               center,                                      // origin
+                                               k,                                           // color 1
+                                               k,                                           // color 2
+                                               0.15,                                        // gravity
+                                               lhrandom(originmins[0], originmaxs[0]),      // offset x
+                                               lhrandom(originmins[1], originmaxs[1]),      // offset y
+                                               lhrandom(originmins[2], originmaxs[2]),      // offset z
+                                               lhrandom(velocitymins[0], velocitymaxs[0]),  // velocity offset x
+                                               lhrandom(velocitymins[1], velocitymaxs[1]),  // velocity offset y
+                                               lhrandom(velocitymins[2], velocitymaxs[2]),  // velocity offset z
+                                               0,                                           // air friction
+                                               0,                                           // liquid friction
+                                               8,                                           // origin jitter
+                                               3,                                           // velocity jitter
+                                               lhrandom(0.1, 0.4)                           // lifetime
+                                       );
                                }
                        }
                }
@@ -1278,7 +1421,7 @@ static void CL_ParticleEffect_Fallback(int effectnameindex, float count, const v
                                        if (cl_particles_quake.integer)
                                        {
                                                color = particlepalette[67 + (rand()&3)];
-                                               CL_NewParticle(center, pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 0, 0.25, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 3, 0, true, 2, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1, 0, 0, NULL);
+                                               CL_NewQuakeParticle(center, color, color, 0.25, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 3, 0, 2);
                                        }
                                        else
                                        {
@@ -1292,7 +1435,7 @@ static void CL_ParticleEffect_Fallback(int effectnameindex, float count, const v
                                        {
                                                dec = 6;
                                                color = particlepalette[67 + (rand()&3)];
-                                               CL_NewParticle(center, pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 0, 0.25, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 3, 0, true, 2, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1, 0, 0, NULL);
+                                               CL_NewQuakeParticle(center, color, color, 0.25, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 3, 0, 2);
                                        }
                                        else
                                        {
@@ -1309,7 +1452,7 @@ static void CL_ParticleEffect_Fallback(int effectnameindex, float count, const v
                                        {
                                                r = rand()&3;
                                                color = particlepalette[ramp3[r]];
-                                               CL_NewParticle(center, pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 0, -0.10, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 3, 0, true, 0.1372549*(6-r), 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1, 0, 0, NULL);
+                                               CL_NewQuakeParticle(center, color, color, -0.10, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 3, 0, 0.1372549 * (6 - r));
                                        }
                                        else
                                        {
@@ -1323,7 +1466,7 @@ static void CL_ParticleEffect_Fallback(int effectnameindex, float count, const v
                                        {
                                                r = 2 + (rand()%4);
                                                color = particlepalette[ramp3[r]];
-                                               CL_NewParticle(center, pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 0, -0.15, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 3, 0, true, 0.1372549*(6-r), 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1, 0, 0, NULL);
+                                               CL_NewQuakeParticle(center, color, color, -0.15, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 3, 0, 0.1372549 * (6 - r));
                                        }
                                        else
                                        {
@@ -1336,8 +1479,8 @@ static void CL_ParticleEffect_Fallback(int effectnameindex, float count, const v
                                        {
                                                dec = 6;
                                                color = particlepalette[52 + (rand()&7)];
-                                               CL_NewParticle(center, pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 0, 0, 0, pos[0], pos[1], pos[2], 30*dir[1], 30*-dir[0], 0, 0, 0, 0, 0, true, 0.5, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1, 0, 0, NULL);
-                                               CL_NewParticle(center, pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 0, 0, 0, pos[0], pos[1], pos[2], 30*-dir[1], 30*dir[0], 0, 0, 0, 0, 0, true, 0.5, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1, 0, 0, NULL);
+                                               CL_NewQuakeParticle(center, color, color, 0, pos[0], pos[1], pos[2], 30*dir[1], 30*-dir[0], 0, 0, 0, 0, 0, 0.5);
+                                               CL_NewQuakeParticle(center, color, color, 0, pos[0], pos[1], pos[2], 30*-dir[1], 30*dir[0], 0, 0, 0, 0, 0, 0.5);
                                        }
                                        else if (gamemode == GAME_GOODVSBAD2)
                                        {
@@ -1356,8 +1499,8 @@ static void CL_ParticleEffect_Fallback(int effectnameindex, float count, const v
                                        {
                                                dec = 6;
                                                color = particlepalette[230 + (rand()&7)];
-                                               CL_NewParticle(center, pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 0, 0, 0, pos[0], pos[1], pos[2], 30*dir[1], 30*-dir[0], 0, 0, 0, 0, 0, true, 0.5, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1, 0, 0, NULL);
-                                               CL_NewParticle(center, pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 0, 0, 0, pos[0], pos[1], pos[2], 30*-dir[1], 30*dir[0], 0, 0, 0, 0, 0, true, 0.5, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1, 0, 0, NULL);
+                                               CL_NewQuakeParticle(center, color, color, 0, pos[0], pos[1], pos[2], 30 *  dir[1], 30 * -dir[0], 0, 0, 0, 0, 0, 0.5);
+                                               CL_NewQuakeParticle(center, color, color, 0, pos[0], pos[1], pos[2], 30 * -dir[1], 30 *  dir[0], 0, 0, 0, 0, 0, 0.5);
                                        }
                                        else
                                        {
@@ -1370,7 +1513,7 @@ static void CL_ParticleEffect_Fallback(int effectnameindex, float count, const v
                                        if (cl_particles_quake.integer)
                                        {
                                                color = particlepalette[152 + (rand()&3)];
-                                               CL_NewParticle(center, pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 0, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 8, 0, true, 0.3, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1, 0, 0, NULL);
+                                               CL_NewQuakeParticle(center, color, color, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 8, 0, 0.3);
                                        }
                                        else if (gamemode == GAME_GOODVSBAD2)
                                        {
@@ -1793,27 +1936,57 @@ void CL_ParticleExplosion (const vec3_t org)
 {
        int i;
        trace_t trace;
-       //vec3_t v;
-       //vec3_t v2;
+       particle_t *particle;
+
        R_Stain(org, 96, 40, 40, 40, 64, 88, 88, 88, 64);
        CL_SpawnDecalParticleForPoint(org, 40, 48, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
 
        if (cl_particles_quake.integer)
        {
-               for (i = 0;i < 1024;i++)
+               for (i = 0; i < 1024; i++)
                {
-                       int r, color;
-                       r = rand()&3;
+                       int color;
+                       int r = rand()&3;
+
                        if (i & 1)
                        {
                                color = particlepalette[ramp1[r]];
-                               CL_NewParticle(org, pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 0, 0, 0, org[0], org[1], org[2], 0, 0, 0, -4, -4, 16, 256, true, 0.1006 * (8 - r), 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1, 0, 0, NULL);
+
+                               particle = CL_NewQuakeParticle(
+                                       org,
+                                       color, color,
+                                       0.05,                        // gravity
+                                       org[0], org[1], org[2],      // offset
+                                       0, 0, 0,                     // velocity
+                                       2,                           // air friction
+                                       0,                           // liquid friction
+                                       16,                          // origin jitter
+                                       256,                         // velocity jitter
+                                       5                            // lifetime
+                               );
+                               particle->typeindex = pt_explode;
                        }
                        else
                        {
                                color = particlepalette[ramp2[r]];
-                               CL_NewParticle(org, pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 0, 0, 0, org[0], org[1], org[2], 0, 0, 0, 1, 1, 16, 256, true, 0.0669 * (8 - r), 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1, 0, 0, NULL);
+
+                               particle = CL_NewQuakeParticle(
+                                       org,
+                                       color, color,
+                                       0.05,                        // gravity
+                                       org[0], org[1], org[2],      // offset
+                                       0, 0, 0,                     // velocity
+                                       0,                           // air friction
+                                       0,                           // liquid friction
+                                       16,                          // origin jitter
+                                       256,                         // velocity jitter
+                                       5                            // lifetime
+                               );
+
+                               particle->typeindex = pt_explode2;
                        }
+
+                       particle->time2 = r;  // time2 is used to progress the colour ramp index
                }
        }
        else
@@ -1867,7 +2040,7 @@ void CL_ParticleExplosion2 (const vec3_t org, int colorStart, int colorLength)
        {
                k = particlepalette[colorStart + (i % colorLength)];
                if (cl_particles_quake.integer)
-                       CL_NewParticle(org, pt_alphastatic, k, k, tex_particle, 1, 0, 255, 0, 0, 0, org[0], org[1], org[2], 0, 0, 0, -4, -4, 16, 256, true, 0.3, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1, 0, 0, NULL);
+                       CL_NewQuakeParticle(org, k, k, 0, org[0], org[1], org[2], 0, 0, 0, -4, -4, 16, 256, 0.3);
                else
                        CL_NewParticle(org, pt_alphastatic, k, k, tex_particle, lhrandom(0.5, 1.5), 0, 255, 512, 0, 0, org[0], org[1], org[2], 0, 0, 0, lhrandom(1.5, 3), lhrandom(1.5, 3), 8, 192, true, 0, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1, 0, 0, NULL);
        }
@@ -1916,7 +2089,9 @@ void CL_ParticleRain (const vec3_t mins, const vec3_t maxs, const vec3_t dir, in
 {
        int k;
        float minz, maxz, lifetime = 30;
+       float particle_size;
        vec3_t org;
+
        if (!cl_particles.integer) return;
        if (dir[2] < 0) // falling
        {
@@ -1939,28 +2114,27 @@ void CL_ParticleRain (const vec3_t mins, const vec3_t maxs, const vec3_t dir, in
        {
        case 0:
                if (!cl_particles_rain.integer) break;
+
                count *= 4; // ick, this should be in the mod or maps?
+               particle_size = (gamemode == GAME_GOODVSBAD2) ? 20 : 0.5;
 
                while(count--)
                {
                        k = particlepalette[colorbase + (rand()&3)];
                        VectorSet(org, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz));
-                       if (gamemode == GAME_GOODVSBAD2)
-                               CL_NewParticle(org, pt_rain, k, k, tex_particle, 20, 0, lhrandom(32, 64), 0, 0, -1, org[0], org[1], org[2], dir[0], dir[1], dir[2], 0, 0, 0, 0, true, lifetime, 1, PBLEND_ADD, PARTICLE_SPARK, -1, -1, -1, 1, 1, 0, 0, NULL);
-                       else
-                               CL_NewParticle(org, pt_rain, k, k, tex_particle, 0.5, 0, lhrandom(32, 64), 0, 0, -1, org[0], org[1], org[2], dir[0], dir[1], dir[2], 0, 0, 0, 0, true, lifetime, 1, PBLEND_ADD, PARTICLE_SPARK, -1, -1, -1, 1, 1, 0, 0, NULL);
+                       CL_NewParticle(org, pt_rain, k, k, tex_particle, particle_size, 0, lhrandom(32, 64), 0, 0, -1, org[0], org[1], org[2], dir[0], dir[1], dir[2], 0, 0, 0, 0, true, lifetime, 1, PBLEND_ADD, PARTICLE_SPARK, -1, -1, -1, 1, 1, 0, 0, NULL);
                }
                break;
        case 1:
                if (!cl_particles_snow.integer) break;
+
+               particle_size = (gamemode == GAME_GOODVSBAD2) ? 20 : 1.0;
+
                while(count--)
                {
                        k = particlepalette[colorbase + (rand()&3)];
                        VectorSet(org, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz));
-                       if (gamemode == GAME_GOODVSBAD2)
-                               CL_NewParticle(org, pt_snow, k, k, tex_particle, 20, 0, lhrandom(64, 128), 0, 0, -1, org[0], org[1], org[2], dir[0], dir[1], dir[2], 0, 0, 0, 0, true, lifetime, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1, 0, 0, NULL);
-                       else
-                               CL_NewParticle(org, pt_snow, k, k, tex_particle, 1, 0, lhrandom(64, 128), 0, 0, -1, org[0], org[1], org[2], dir[0], dir[1], dir[2], 0, 0, 0, 0, true, lifetime, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1, 0, 0, NULL);
+                       CL_NewParticle(org, pt_snow, k, k, tex_particle, 1, 0, lhrandom(64, 128), 0, 0, -1, org[0], org[1], org[2], dir[0], dir[1], dir[2], 0, 0, 0, 0, true, lifetime, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1, 0, 0, NULL);
                }
                break;
        default:
@@ -2357,7 +2531,7 @@ static void R_InitParticleTexture (void)
 
                        if (COM_ParseToken_Simple(&bufptr, true, false, true) && strcmp(com_token, "\n"))
                        {
-                               strlcpy(texturename, com_token, sizeof(texturename));
+                               dp_strlcpy(texturename, com_token, sizeof(texturename));
                                s1 = atof(com_token);
                                if (COM_ParseToken_Simple(&bufptr, true, false, true) && strcmp(com_token, "\n"))
                                {
@@ -2369,9 +2543,9 @@ static void R_InitParticleTexture (void)
                                                if (COM_ParseToken_Simple(&bufptr, true, false, true) && strcmp(com_token, "\n"))
                                                {
                                                        t2 = atof(com_token);
-                                                       strlcpy(texturename, "particles/particlefont.tga", sizeof(texturename));
+                                                       dp_strlcpy(texturename, "particles/particlefont.tga", sizeof(texturename));
                                                        if (COM_ParseToken_Simple(&bufptr, true, false, true) && strcmp(com_token, "\n"))
-                                                               strlcpy(texturename, com_token, sizeof(texturename));
+                                                               dp_strlcpy(texturename, com_token, sizeof(texturename));
                                                }
                                        }
                                }
@@ -2741,10 +2915,16 @@ void R_DrawParticles (void)
        int hitent;
        trace_t trace;
        qbool update;
+       float pt_explode_frame_interval, pt_explode2_frame_interval;
+       int color;
 
        frametime = bound(0, cl.time - cl.particles_updatetime, 1);
        cl.particles_updatetime = bound(cl.time - 1, cl.particles_updatetime + frametime, cl.time + 1);
 
+       // Handling of the colour ramp for pt_explode and pt_explode2
+       pt_explode_frame_interval = frametime * 10;
+       pt_explode2_frame_interval = frametime * 15;
+
        // LadyHavoc: early out conditions
        if (!cl.num_particles)
                return;
@@ -2919,7 +3099,35 @@ void R_DrawParticles (void)
                                        a = CL_PointSuperContents(p->org);
                                        if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK))
                                                goto killparticle;
-                                       break;
+                                       case pt_explode:
+                                               // Progress the particle colour up the ramp
+                                               p->time2 += pt_explode_frame_interval;
+                                               if (p->time2 >= 8)
+                                               {
+                                                       p->die = -1;
+                                               }
+                                               else {
+                                                       color = particlepalette[ramp1[(int)p->time2]];
+                                                       p->color[0] = color >> 16;
+                                                       p->color[1] = color >>  8;
+                                                       p->color[2] = color >>  0;
+                                               }
+                                               break;
+
+                                       case pt_explode2:
+                                               // Progress the particle colour up the ramp
+                                               p->time2 += pt_explode2_frame_interval;
+                                               if (p->time2 >= 8)
+                                               {
+                                                       p->die = -1;
+                                               }
+                                               else {
+                                                       color = particlepalette[ramp2[(int)p->time2]];
+                                                       p->color[0] = color >> 16;
+                                                       p->color[1] = color >>  8;
+                                                       p->color[2] = color >>  0;
+                                               }
+                                               break;
                                default:
                                        break;
                                }
index 8e07e19e79a40b7e79c413a9856b26bfee302067..ed8bb320fdd04891b19bdf9fb7886ee4d0c90b83 100644 (file)
@@ -58,7 +58,22 @@ particletype_t;
 
 typedef enum ptype_e
 {
-       pt_dead, pt_alphastatic, pt_static, pt_spark, pt_beam, pt_rain, pt_raindecal, pt_snow, pt_bubble, pt_blood, pt_smoke, pt_decal, pt_entityparticle, pt_total
+       pt_dead,
+       pt_alphastatic,
+       pt_static,
+       pt_spark,
+       pt_beam,
+       pt_rain,
+       pt_raindecal,
+       pt_snow,
+       pt_bubble,
+       pt_blood,
+       pt_smoke,
+       pt_decal,
+       pt_entityparticle,
+       pt_explode,   // used for Quake-style explosion particle colour ramping
+       pt_explode2,  // used for Quake-style explosion particle colour ramping
+       pt_total
 }
 ptype_t;
 
@@ -108,6 +123,7 @@ void CL_Particles_Clear(void);
 void CL_Particles_Init(void);
 void CL_Particles_Shutdown(void);
 particle_t *CL_NewParticle(const vec3_t sortorigin, unsigned short ptypeindex, int pcolor1, int pcolor2, int ptex, float psize, float psizeincrease, float palpha, float palphafade, float pgravity, float pbounce, float px, float py, float pz, float pvx, float pvy, float pvz, float pairfriction, float pliquidfriction, float originjitter, float velocityjitter, qbool pqualityreduction, float lifetime, float stretch, pblend_t blendmode, porientation_t orientation, int staincolor1, int staincolor2, int staintex, float stainalpha, float stainsize, float angle, float spin, float tint[4]);
+particle_t *CL_NewQuakeParticle(const vec3_t origin, const int color_1, const int color_2, const float gravity, const float offset_x, const float offset_y, const float offset_z, const float velocity_offset_x, const float velocity_offset_y, const float velocity_offset_z, const float air_friction, const float liquid_friction, const float origin_jitter, const float velocity_jitter, const float lifetime);
 
 typedef enum effectnameindex_s
 {
index 44b493e4771d4105abb1e3750fe54951a9f174f3..04d25a01b946241eb92e92288b73f103390275bc 100644 (file)
@@ -23,7 +23,8 @@ cvar_t scr_conalphafactor = {CF_CLIENT | CF_ARCHIVE, "scr_conalphafactor", "1",
 cvar_t scr_conalpha2factor = {CF_CLIENT | CF_ARCHIVE, "scr_conalpha2factor", "0", "opacity of console background gfx/conback2 relative to scr_conalpha; when 0, gfx/conback2 is not drawn"};
 cvar_t scr_conalpha3factor = {CF_CLIENT | CF_ARCHIVE, "scr_conalpha3factor", "0", "opacity of console background gfx/conback3 relative to scr_conalpha; when 0, gfx/conback3 is not drawn"};
 cvar_t scr_conbrightness = {CF_CLIENT | CF_ARCHIVE, "scr_conbrightness", "1", "brightness of console background (0 = black, 1 = image)"};
-cvar_t scr_conforcewhiledisconnected = {CF_CLIENT, "scr_conforcewhiledisconnected", "1", "forces fullscreen console while disconnected"};
+cvar_t scr_conforcewhiledisconnected = {CF_CLIENT, "scr_conforcewhiledisconnected", "1", "1 forces fullscreen console while disconnected, 2 also forces it when the listen server has started but the client is still loading"};
+cvar_t scr_conheight = {CF_CLIENT | CF_ARCHIVE, "scr_conheight", "0.5", "fraction of screen height occupied by console (reduced as necessary for visibility of loading progress and infobar)"};
 cvar_t scr_conscroll_x = {CF_CLIENT | CF_ARCHIVE, "scr_conscroll_x", "0", "scroll speed of gfx/conback in x direction"};
 cvar_t scr_conscroll_y = {CF_CLIENT | CF_ARCHIVE, "scr_conscroll_y", "0", "scroll speed of gfx/conback in y direction"};
 cvar_t scr_conscroll2_x = {CF_CLIENT | CF_ARCHIVE, "scr_conscroll2_x", "0", "scroll speed of gfx/conback2 in x direction"};
@@ -48,7 +49,7 @@ cvar_t scr_loadingscreen_count = {CF_CLIENT, "scr_loadingscreen_count","1", "num
 cvar_t scr_loadingscreen_firstforstartup = {CF_CLIENT, "scr_loadingscreen_firstforstartup","0", "remove loading.tga from random scr_loadingscreen_count selection and only display it on client startup, 0 = normal, 1 = firstforstartup"};
 cvar_t scr_loadingscreen_barcolor = {CF_CLIENT, "scr_loadingscreen_barcolor", "0 0 1", "rgb color of loadingscreen progress bar"};
 cvar_t scr_loadingscreen_barheight = {CF_CLIENT, "scr_loadingscreen_barheight", "8", "the height of the loadingscreen progress bar"};
-cvar_t scr_loadingscreen_maxfps = {CF_CLIENT, "scr_loadingscreen_maxfps", "10", "restrict maximal FPS for loading screen so it will not update very often (this will make lesser loading times on a maps loading large number of models)"};
+cvar_t scr_loadingscreen_maxfps = {CF_CLIENT, "scr_loadingscreen_maxfps", "20", "maximum FPS for loading screen so it will not update very often (this reduces loading time with lots of models)"};
 cvar_t scr_infobar_height = {CF_CLIENT, "scr_infobar_height", "8", "the height of the infobar items"};
 cvar_t vid_conwidthauto = {CF_CLIENT | CF_ARCHIVE, "vid_conwidthauto", "1", "automatically update vid_conwidth to match aspect ratio"};
 cvar_t vid_conwidth = {CF_CLIENT | CF_ARCHIVE, "vid_conwidth", "640", "virtual width of 2D graphics system (note: changes may be overwritten, see vid_conwidthauto)"};
@@ -99,8 +100,10 @@ int jpeg_supported = false;
 
 qbool  scr_initialized;                // ready to draw
 
-float          scr_con_current;
-int                    scr_con_margin_bottom;
+qbool scr_loading = false;  // we are in a loading screen
+
+unsigned int        scr_con_current;
+static unsigned int scr_con_margin_bottom;
 
 extern int     con_vislines;
 
@@ -139,7 +142,7 @@ for a few moments
 */
 void SCR_CenterPrint(const char *str)
 {
-       strlcpy (scr_centerstring, str, sizeof (scr_centerstring));
+       dp_strlcpy (scr_centerstring, str, sizeof (scr_centerstring));
        scr_centertime_off = scr_centertime.value;
        scr_centertime_start = cl.time;
 
@@ -153,6 +156,48 @@ void SCR_CenterPrint(const char *str)
        }
 }
 
+/*
+============
+SCR_Centerprint_f
+
+Print something to the center of the screen using SCR_Centerprint
+============
+*/
+static void SCR_Centerprint_f (cmd_state_t *cmd)
+{
+       char msg[MAX_INPUTLINE];
+       unsigned int i, c, p;
+       c = Cmd_Argc(cmd);
+       if(c >= 2)
+       {
+               dp_strlcpy(msg, Cmd_Argv(cmd,1), sizeof(msg));
+               for(i = 2; i < c; ++i)
+               {
+                       dp_strlcat(msg, " ", sizeof(msg));
+                       dp_strlcat(msg, Cmd_Argv(cmd, i), sizeof(msg));
+               }
+               c = (unsigned int)strlen(msg);
+               for(p = 0, i = 0; i < c; ++i)
+               {
+                       if(msg[i] == '\\')
+                       {
+                               if(msg[i+1] == 'n')
+                                       msg[p++] = '\n';
+                               else if(msg[i+1] == '\\')
+                                       msg[p++] = '\\';
+                               else {
+                                       msg[p++] = '\\';
+                                       msg[p++] = msg[i+1];
+                               }
+                               ++i;
+                       } else {
+                               msg[p++] = msg[i];
+                       }
+               }
+               msg[p] = '\0';
+               SCR_CenterPrint(msg);
+       }
+}
 
 static void SCR_DrawCenterString (void)
 {
@@ -590,11 +635,13 @@ SCR_DrawInfobar
 */
 static void SCR_DrawInfobar(void)
 {
-       int offset = 0;
+       unsigned int offset = 0;
        offset += SCR_DrawQWDownload(offset);
        offset += SCR_DrawCurlDownload(offset);
        if(scr_infobartime_off > 0)
                offset += SCR_DrawInfobarString(offset);
+       if(!offset && scr_loading)
+               offset = scr_loadingscreen_barheight.integer;
        if(offset != scr_con_margin_bottom)
                Con_DPrintf("broken console margin calculation: %d != %d\n", offset, scr_con_margin_bottom);
 }
@@ -635,7 +682,7 @@ static void SCR_InfoBar_f(cmd_state_t *cmd)
        if(Cmd_Argc(cmd) == 3)
        {
                scr_infobartime_off = atof(Cmd_Argv(cmd, 1));
-               strlcpy(scr_infobarstring, Cmd_Argv(cmd, 2), sizeof(scr_infobarstring));
+               dp_strlcpy(scr_infobarstring, Cmd_Argv(cmd, 2), sizeof(scr_infobarstring));
        }
        else
        {
@@ -651,8 +698,6 @@ SCR_SetUpToDrawConsole
 */
 static void SCR_SetUpToDrawConsole (void)
 {
-       // lines of console to display
-       float conlines;
 #ifdef CONFIG_MENU
        static int framecounter = 0;
 #endif
@@ -678,13 +723,11 @@ static void SCR_SetUpToDrawConsole (void)
        else
                key_consoleactive &= ~KEY_CONSOLEACTIVE_FORCED;
 
-// decide on the height of the console
+       // decide on the height of the console
        if (key_consoleactive & KEY_CONSOLEACTIVE_USER)
-               conlines = vid_conheight.integer/2;     // half screen
+               scr_con_current = vid_conheight.integer * scr_conheight.value;
        else
-               conlines = 0;                           // none visible
-
-       scr_con_current = conlines;
+               scr_con_current = 0; // none visible
 }
 
 /*
@@ -694,37 +737,21 @@ SCR_DrawConsole
 */
 void SCR_DrawConsole (void)
 {
+       // infobar and loading progress are not drawn simultaneously
        scr_con_margin_bottom = SCR_InfobarHeight();
+       if (!scr_con_margin_bottom && scr_loading)
+               scr_con_margin_bottom = scr_loadingscreen_barheight.integer;
        if (key_consoleactive & KEY_CONSOLEACTIVE_FORCED)
        {
                // full screen
                Con_DrawConsole (vid_conheight.integer - scr_con_margin_bottom);
        }
        else if (scr_con_current)
-               Con_DrawConsole (min((int)scr_con_current, vid_conheight.integer - scr_con_margin_bottom));
+               Con_DrawConsole (min(scr_con_current, vid_conheight.integer - scr_con_margin_bottom));
        else
                con_vislines = 0;
 }
 
-qbool scr_loading = false;
-
-/*
-===============
-SCR_BeginLoadingPlaque
-
-================
-*/
-void SCR_BeginLoadingPlaque (qbool startup)
-{
-       scr_loading = true;
-       SCR_UpdateLoadingScreen(false, startup);
-}
-
-void SCR_EndLoadingPlaque(void)
-{
-       scr_loading = false;
-}
-
 //=============================================================================
 
 /*
@@ -779,6 +806,7 @@ void CL_Screen_Init(void)
        Cvar_RegisterVariable (&scr_conscroll3_y);
        Cvar_RegisterVariable (&scr_conbrightness);
        Cvar_RegisterVariable (&scr_conforcewhiledisconnected);
+       Cvar_RegisterVariable (&scr_conheight);
 #ifdef CONFIG_MENU
        Cvar_RegisterVariable (&scr_menuforcewhiledisconnected);
 #endif
@@ -856,6 +884,7 @@ void CL_Screen_Init(void)
        if (Sys_CheckParm ("-noconsole"))
                Cvar_SetQuick(&scr_conforcewhiledisconnected, "0");
 
+       Cmd_AddCommand(CF_CLIENT, "cprint", SCR_Centerprint_f, "print something at the screen center");
        Cmd_AddCommand(CF_CLIENT, "sizeup",SCR_SizeUp_f, "increase view size (increases viewsize cvar)");
        Cmd_AddCommand(CF_CLIENT, "sizedown",SCR_SizeDown_f, "decrease view size (decreases viewsize cvar)");
        Cmd_AddCommand(CF_CLIENT, "screenshot",SCR_ScreenShot_f, "takes a screenshot of the next rendered frame");
@@ -889,7 +918,7 @@ void SCR_ScreenShot_f(cmd_state_t *cmd)
        if (Cmd_Argc(cmd) == 2)
        {
                const char *ext;
-               strlcpy(filename, Cmd_Argv(cmd, 1), sizeof(filename));
+               dp_strlcpy(filename, Cmd_Argv(cmd, 1), sizeof(filename));
                ext = FS_FileExtension(filename);
                if (!strcasecmp(ext, "jpg"))
                {
@@ -982,7 +1011,7 @@ void SCR_ScreenShot_f(cmd_state_t *cmd)
                {
                        if(SCR_ScreenShot (filename, buffer1, buffer2, 0, 0, vid.width, vid.height, false, false, false, false, false, true, scr_screenshot_alpha.integer != 0))
                        {
-                               strlcpy(filename + strlen(filename) - 3, "tga", 4);
+                               dp_strlcpy(filename + strlen(filename) - 3, "tga", 4);
                                Con_Printf("Wrote %s\n", filename);
                        }
                }
@@ -1197,19 +1226,13 @@ static void SCR_CaptureVideo_VideoFrame(int newframestepframenum)
        cls.capturevideo.videoframes(newframestepframenum - cls.capturevideo.framestepframe);
        cls.capturevideo.framestepframe = newframestepframenum;
 
-       if(cl_capturevideo_printfps.integer)
+       if(cl_capturevideo_printfps.integer && host.realtime > cls.capturevideo.lastfpstime + 1)
        {
-               char buf[80];
-               double t = host.realtime;
-               if(t > cls.capturevideo.lastfpstime + 1)
-               {
-                       double fps1 = (cls.capturevideo.frame - cls.capturevideo.lastfpsframe) / (t - cls.capturevideo.lastfpstime + 0.0000001);
-                       double fps  = (cls.capturevideo.frame                                ) / (t - cls.capturevideo.starttime   + 0.0000001);
-                       dpsnprintf(buf, sizeof(buf), "capturevideo: (%.1fs) last second %.3ffps, total %.3ffps\n", cls.capturevideo.frame / cls.capturevideo.framerate, fps1, fps);
-                       Sys_Print(buf);
-                       cls.capturevideo.lastfpstime = t;
-                       cls.capturevideo.lastfpsframe = cls.capturevideo.frame;
-               }
+               double fps1 = (cls.capturevideo.frame - cls.capturevideo.lastfpsframe) / (host.realtime - cls.capturevideo.lastfpstime + 0.0000001);
+               double fps  = (cls.capturevideo.frame                                ) / (host.realtime - cls.capturevideo.starttime   + 0.0000001);
+               Sys_Printf("capturevideo: (%.1fs) last second %.3ffps, total %.3ffps\n", cls.capturevideo.frame / cls.capturevideo.framerate, fps1, fps);
+               cls.capturevideo.lastfpstime = host.realtime;
+               cls.capturevideo.lastfpsframe = cls.capturevideo.frame;
        }
 }
 
@@ -1313,7 +1336,7 @@ static void R_Envmap_f(cmd_state_t *cmd)
                return;
        }
 
-       strlcpy (basename, Cmd_Argv(cmd, 1), sizeof (basename));
+       dp_strlcpy (basename, Cmd_Argv(cmd, 1), sizeof (basename));
        size = atoi(Cmd_Argv(cmd, 2));
        if (size != 128 && size != 256 && size != 512 && size != 1024)
        {
@@ -1389,8 +1412,8 @@ void SHOWLMP_decodeshow(void)
        int k;
        char lmplabel[256], picname[256];
        float x, y;
-       strlcpy (lmplabel,MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof (lmplabel));
-       strlcpy (picname, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof (picname));
+       dp_strlcpy (lmplabel,MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof (lmplabel));
+       dp_strlcpy (picname, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof (picname));
        if (gamemode == GAME_NEHAHRA) // LadyHavoc: nasty old legacy junk
        {
                x = MSG_ReadByte(&cl_message);
@@ -1421,8 +1444,8 @@ void SHOWLMP_decodeshow(void)
                        if (!cl.showlmps[k].isactive)
                                break;
        cl.showlmps[k].isactive = true;
-       strlcpy (cl.showlmps[k].label, lmplabel, sizeof (cl.showlmps[k].label));
-       strlcpy (cl.showlmps[k].pic, picname, sizeof (cl.showlmps[k].pic));
+       dp_strlcpy (cl.showlmps[k].label, lmplabel, sizeof (cl.showlmps[k].label));
+       dp_strlcpy (cl.showlmps[k].pic, picname, sizeof (cl.showlmps[k].pic));
        cl.showlmps[k].x = x;
        cl.showlmps[k].y = y;
        cl.num_showlmps = max(cl.num_showlmps, k + 1);
@@ -1570,32 +1593,27 @@ typedef struct loadingscreenstack_s
 }
 loadingscreenstack_t;
 static loadingscreenstack_t *loadingscreenstack = NULL;
-static qbool loadingscreendone = false;
-static qbool loadingscreencleared = false;
-static float loadingscreenheight = 0;
-rtexture_t *loadingscreentexture = NULL;
+rtexture_t *loadingscreentexture = NULL; // last framebuffer before loading screen, kept for the background
 static float loadingscreentexture_vertex3f[12];
 static float loadingscreentexture_texcoord2f[8];
 static int loadingscreenpic_number = 0;
+/// User-friendly connection status for the menu and/or loading screen,
+/// colours and \n not supported.
+char cl_connect_status[MAX_QPATH]; // should match size of loadingscreenstack_t msg[]
 
 static void SCR_DrawLoadingScreen(void);
 static void SCR_DrawScreen (void)
 {
        Draw_Frame();
-
+       DrawQ_Start();
        R_Mesh_Start();
-
        R_UpdateVariables();
 
-       // this will be set back to 0 by R_RenderView during CL_VM_UpdateView
-       r_refdef.draw2dstage = 1;
-       R_ResetViewRendering2D_Common(0, NULL, NULL, 0, 0, vid.width, vid.height, vid_conwidth.integer, vid_conheight.integer);
-
        // Quake uses clockwise winding, so these are swapped
        r_refdef.view.cullface_front = GL_BACK;
        r_refdef.view.cullface_back = GL_FRONT;
 
-       if (cls.signon == SIGNONS)
+       if (!scr_loading && cls.signon == SIGNONS)
        {
                float size;
 
@@ -1664,7 +1682,7 @@ static void SCR_DrawScreen (void)
 
                // if CSQC is loaded, it is required to provide the CSQC_UpdateView function,
                // and won't render a view if it does not call that.
-               if (cl.csqc_loaded)
+               if (CLVM_prog->loaded && !(CLVM_prog->flag & PRVM_CSQC_SIMPLE))
                        CL_VM_UpdateView(r_stereo_side ? 0.0 : max(0.0, cl.time - cl.oldtime));
                else
                {
@@ -1675,19 +1693,6 @@ static void SCR_DrawScreen (void)
                        R_RenderView(0, NULL, NULL, r_refdef.view.x, r_refdef.view.y, r_refdef.view.width, r_refdef.view.height);
                }
        }
-       else if (key_dest == key_game && key_consoleactive == 0 && (cls.state == ca_connected || cls.connect_trying))
-       {
-               // draw the loading screen for a while if we're still connecting and not forcing the console or menu to show up
-               char temp[64];
-               if (cls.signon > 0)
-                       SCR_PushLoadingScreen(va(temp, sizeof(temp), "Connect: Signon stage %i of %i", cls.signon, SIGNONS), 1.0);
-               else if (cls.connect_remainingtries > 0)
-                       SCR_PushLoadingScreen(va(temp, sizeof(temp), "Connect: Trying...  %i", cls.connect_remainingtries), 1.0);
-               else
-                       SCR_PushLoadingScreen(va(temp, sizeof(temp), "Connect: Waiting %i seconds for reply", 10 + cls.connect_remainingtries), 1.0);
-               SCR_DrawLoadingScreen();
-               SCR_PopLoadingScreen(false);
-       }
 
        // Don't apply debugging stuff like r_showsurfaces to the UI
        r_refdef.view.showdebug = false;
@@ -1737,10 +1742,6 @@ static void SCR_DrawScreen (void)
 
        // draw 2D stuff
 
-       // Don't flicker when starting a local server.
-       if(scr_loading && !loadingscreenstack && ((!cls.signon && !sv.active) || (cls.signon == SIGNONS)))
-               SCR_EndLoadingPlaque();
-
        if(!scr_con_current && !(key_consoleactive & KEY_CONSOLEACTIVE_FORCED))
                if ((key_dest == key_game || key_dest == key_message) && !r_letterbox.value && !scr_loading)
                        Con_DrawNotify ();      // only draw notify in game
@@ -1750,13 +1751,17 @@ static void SCR_DrawScreen (void)
        else
                host.paused = false;
 
-       if (cls.signon == SIGNONS)
+       if (!scr_loading && cls.signon == SIGNONS)
        {
                SCR_DrawNet ();
                SCR_DrawTurtle ();
                SCR_DrawPause ();
                if (!r_letterbox.value)
+               {
                        Sbar_Draw();
+                       if (CLVM_prog->loaded && CLVM_prog->flag & PRVM_CSQC_SIMPLE)
+                               CL_VM_DrawHud(r_stereo_side ? 0.0 : max(0.0, cl.time - cl.oldtime));
+               }
                SHOWLMP_drawall();
                SCR_CheckDrawCenterString();
        }
@@ -1768,13 +1773,29 @@ static void SCR_DrawScreen (void)
        CL_DrawVideo();
        R_Shadow_EditLights_DrawSelectedLightProperties();
 
-       SCR_DrawConsole();
-       
-       if(!scr_loading) {
-               SCR_DrawBrand();
+       if (scr_loading)
+       {
+               // connect_status replaces any dummy_status
+               if ((!loadingscreenstack || loadingscreenstack->msg[0] == '\0') && cl_connect_status[0] != '\0')
+               {
+                       loadingscreenstack_t connect_status, *og_ptr = loadingscreenstack;
 
-               SCR_DrawInfobar();
+                       connect_status.absolute_loading_amount_min = 0;
+                       dp_strlcpy(connect_status.msg, cl_connect_status, sizeof(cl_connect_status));
+                       loadingscreenstack = &connect_status;
+                       SCR_DrawLoadingScreen();
+                       loadingscreenstack = og_ptr;
+               }
+               else
+                       SCR_DrawLoadingScreen();
+       }
 
+       SCR_DrawConsole();
+       SCR_DrawInfobar();
+
+       if (!scr_loading)
+       {
+               SCR_DrawBrand();
                SCR_DrawTouchscreenOverlay();
        }
        if (r_timereport_active)
@@ -1786,9 +1807,8 @@ static void SCR_DrawScreen (void)
        if(!scr_loading)
                Sbar_ShowFPS();
 
-       DrawQ_Finish();
-
        R_Mesh_Finish();
+       DrawQ_Finish();
        R_RenderTarget_FreeUnused(false);
 }
 
@@ -1825,19 +1845,67 @@ static void SCR_SetLoadingScreenTexture(void)
        loadingscreentexture_texcoord2f[6] = 0;loadingscreentexture_texcoord2f[7] = 0;
 }
 
-void SCR_UpdateLoadingScreenIfShown(void)
+static void SCR_ChooseLoadingPic(qbool startup)
 {
-       if(loadingscreendone)
-               SCR_UpdateLoadingScreen(loadingscreencleared, false);
+       if(startup && scr_loadingscreen_firstforstartup.integer)
+               loadingscreenpic_number = 0;
+       else if(scr_loadingscreen_firstforstartup.integer)
+               if(scr_loadingscreen_count.integer > 1)
+                       loadingscreenpic_number = rand() % (scr_loadingscreen_count.integer - 1) + 1;
+               else
+                       loadingscreenpic_number = 0;
+       else
+               loadingscreenpic_number = rand() % (scr_loadingscreen_count.integer > 1 ? scr_loadingscreen_count.integer : 1);
 }
 
+/*
+===============
+SCR_BeginLoadingPlaque
+
+================
+*/
+void SCR_BeginLoadingPlaque(qbool startup)
+{
+       loadingscreenstack_t dummy_status;
+
+       // we need to push a dummy status so CL_UpdateScreen knows we have things to load...
+       if (!loadingscreenstack)
+       {
+               dummy_status.msg[0] = '\0';
+               dummy_status.absolute_loading_amount_min = 0;
+               loadingscreenstack = &dummy_status;
+       }
+
+       SCR_DeferLoadingPlaque(startup);
+       if (scr_loadingscreen_background.integer)
+               SCR_SetLoadingScreenTexture();
+       CL_UpdateScreen();
+
+       if (loadingscreenstack == &dummy_status)
+               loadingscreenstack = NULL;
+}
+
+void SCR_DeferLoadingPlaque(qbool startup)
+{
+       SCR_ChooseLoadingPic(startup);
+       scr_loading = true;
+}
+
+void SCR_EndLoadingPlaque(void)
+{
+       scr_loading = false;
+       SCR_ClearLoadingScreenTexture();
+}
+
+//=============================================================================
+
 void SCR_PushLoadingScreen (const char *msg, float len_in_parent)
 {
        loadingscreenstack_t *s = (loadingscreenstack_t *) Z_Malloc(sizeof(loadingscreenstack_t));
        s->prev = loadingscreenstack;
        loadingscreenstack = s;
 
-       strlcpy(s->msg, msg, sizeof(s->msg));
+       dp_strlcpy(s->msg, msg, sizeof(s->msg));
        s->relative_completion = 0;
 
        if(s->prev)
@@ -1853,7 +1921,8 @@ void SCR_PushLoadingScreen (const char *msg, float len_in_parent)
                s->absolute_loading_amount_len = 1;
        }
 
-               SCR_UpdateLoadingScreenIfShown();
+       if (scr_loading)
+               CL_UpdateScreen();
 }
 
 void SCR_PopLoadingScreen (qbool redraw)
@@ -1871,8 +1940,8 @@ void SCR_PopLoadingScreen (qbool redraw)
                s->prev->relative_completion = (s->absolute_loading_amount_min + s->absolute_loading_amount_len - s->prev->absolute_loading_amount_min) / s->prev->absolute_loading_amount_len;
        Z_Free(s);
 
-       if(redraw)
-               SCR_UpdateLoadingScreenIfShown();
+       if (scr_loading && redraw)
+               CL_UpdateScreen();
 }
 
 void SCR_ClearLoadingScreen (qbool redraw)
@@ -1898,7 +1967,6 @@ static float SCR_DrawLoadingStack_r(loadingscreenstack_t *s, float y, float size
                        len = strlen(s->msg);
                        x = (vid_conwidth.integer - DrawQ_TextWidth(s->msg, len, size, size, true, FONT_INFOBAR)) / 2;
                        y -= size;
-                       DrawQ_Fill(0, y, vid_conwidth.integer, size, 0, 0, 0, 1, 0);
                        DrawQ_String(x, y, s->msg, len, size, size, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
                        total += size;
                }
@@ -1909,7 +1977,6 @@ static float SCR_DrawLoadingStack_r(loadingscreenstack_t *s, float y, float size
                len = strlen(s->msg);
                x = (vid_conwidth.integer - DrawQ_TextWidth(s->msg, len, size, size, true, FONT_INFOBAR)) / 2;
                y -= size;
-               DrawQ_Fill(0, y, vid_conwidth.integer, size, 0, 0, 0, 1, 0);
                DrawQ_String(x, y, s->msg, len, size, size, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
                total += size;
        }
@@ -1922,7 +1989,7 @@ static void SCR_DrawLoadingStack(void)
        float verts[12];
        float colors[16];
 
-       loadingscreenheight = SCR_DrawLoadingStack_r(loadingscreenstack, vid_conheight.integer, scr_loadingscreen_barheight.value);
+       SCR_DrawLoadingStack_r(loadingscreenstack, vid_conheight.integer, scr_loadingscreen_barheight.value);
        if(loadingscreenstack)
        {
                // height = 32; // sorry, using the actual one is ugly
@@ -1930,7 +1997,7 @@ static void SCR_DrawLoadingStack(void)
                GL_DepthRange(0, 1);
                GL_PolygonOffset(0, 0);
                GL_DepthTest(false);
-//             R_Mesh_ResetTextureState();
+               //R_Mesh_ResetTextureState();
                verts[2] = verts[5] = verts[8] = verts[11] = 0;
                verts[0] = verts[9] = 0;
                verts[1] = verts[4] = vid_conheight.integer - scr_loadingscreen_barheight.value;
@@ -1940,9 +2007,6 @@ static void SCR_DrawLoadingStack(void)
 #if _MSC_VER >= 1400
 #define sscanf sscanf_s
 #endif
-               //                                        ^^^^^^^^^^ blue component
-               //                              ^^^^^^ bottom row
-               //          ^^^^^^^^^^^^ alpha is always on
                colors[0] = 0; colors[1] = 0; colors[2] = 0; colors[3] = 1;
                colors[4] = 0; colors[5] = 0; colors[6] = 0; colors[7] = 1;
                sscanf(scr_loadingscreen_barcolor.string, "%f %f %f", &colors[8], &colors[9], &colors[10]); colors[11] = 1;
@@ -1951,40 +2015,31 @@ static void SCR_DrawLoadingStack(void)
                R_Mesh_PrepareVertices_Generic_Arrays(4, verts, colors, NULL);
                R_SetupShader_Generic_NoTexture(true, true);
                R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
-
-               // make sure everything is cleared, including the progress indicator
-               if(loadingscreenheight < 8)
-                       loadingscreenheight = 8;
        }
 }
 
-static cachepic_t *loadingscreenpic;
-static float loadingscreenpic_vertex3f[12];
-static float loadingscreenpic_texcoord2f[8];
-
-static void SCR_DrawLoadingScreen_SharedSetup (qbool clear)
+static void SCR_DrawLoadingScreen (void)
 {
-       r_viewport_t viewport;
+       cachepic_t *loadingscreenpic;
+       float loadingscreenpic_vertex3f[12];
+       float loadingscreenpic_texcoord2f[8];
        float x, y, w, h, sw, sh, f;
        char vabuf[1024];
-       // release mouse grab while loading
-       if (!vid.fullscreen)
-               VID_SetMouse(false, false, false);
-//     CHECKGLERROR
-       r_refdef.draw2dstage = true;
-       R_Viewport_InitOrtho(&viewport, &identitymatrix, 0, 0, vid.width, vid.height, 0, 0, vid_conwidth.integer, vid_conheight.integer, -10, 100, NULL);
-       R_Mesh_SetRenderTargets(0, NULL, NULL, NULL, NULL, NULL);
-       R_SetViewport(&viewport);
-       GL_ColorMask(1,1,1,1);
-       // when starting up a new video mode, make sure the screen is cleared to black
-       if (clear || loadingscreentexture)
-               GL_Clear(GL_COLOR_BUFFER_BIT, NULL, 1.0f, 0);
-       R_Textures_Frame();
-       R_Mesh_Start();
-       R_EntityMatrix(&identitymatrix);
-       // draw the loading plaque
-       loadingscreenpic = Draw_CachePic_Flags (loadingscreenpic_number ? va(vabuf, sizeof(vabuf), "%s%d", scr_loadingscreen_picture.string, loadingscreenpic_number+1) : scr_loadingscreen_picture.string, loadingscreenpic_number ? CACHEPICFLAG_NOTPERSISTENT : 0);
 
+       GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+       GL_DepthRange(0, 1);
+       GL_PolygonOffset(0, 0);
+       GL_DepthTest(false);
+       GL_Color(1,1,1,1);
+
+       if(loadingscreentexture)
+       {
+               R_Mesh_PrepareVertices_Generic_Arrays(4, loadingscreentexture_vertex3f, NULL, loadingscreentexture_texcoord2f);
+               R_SetupShader_Generic(loadingscreentexture, false, true, true);
+               R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
+       }
+
+       loadingscreenpic = Draw_CachePic_Flags(loadingscreenpic_number ? va(vabuf, sizeof(vabuf), "%s%d", scr_loadingscreen_picture.string, loadingscreenpic_number+1) : scr_loadingscreen_picture.string, loadingscreenpic_number ? CACHEPICFLAG_NOTPERSISTENT : 0);
        w = Draw_GetPicWidth(loadingscreenpic);
        h = Draw_GetPicHeight(loadingscreenpic);
 
@@ -2035,117 +2090,12 @@ static void SCR_DrawLoadingScreen_SharedSetup (qbool clear)
        loadingscreenpic_texcoord2f[2] = 1;loadingscreenpic_texcoord2f[3] = 0;
        loadingscreenpic_texcoord2f[4] = 1;loadingscreenpic_texcoord2f[5] = 1;
        loadingscreenpic_texcoord2f[6] = 0;loadingscreenpic_texcoord2f[7] = 1;
-}
 
-static void SCR_DrawLoadingScreen (void)
-{
-       // we only need to draw the image if it isn't already there
-       GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-       GL_DepthRange(0, 1);
-       GL_PolygonOffset(0, 0);
-       GL_DepthTest(false);
-//     R_Mesh_ResetTextureState();
-       GL_Color(1,1,1,1);
-       if(loadingscreentexture)
-       {
-               R_Mesh_PrepareVertices_Generic_Arrays(4, loadingscreentexture_vertex3f, NULL, loadingscreentexture_texcoord2f);
-               R_SetupShader_Generic(loadingscreentexture, false, true, true);
-               R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
-       }
        R_Mesh_PrepareVertices_Generic_Arrays(4, loadingscreenpic_vertex3f, NULL, loadingscreenpic_texcoord2f);
        R_SetupShader_Generic(Draw_GetPicTexture(loadingscreenpic), true, true, false);
        R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
-       SCR_DrawLoadingStack();
-}
-
-static double loadingscreen_lastupdate;
-
-static void SCR_UpdateVars(void);
-
-void SCR_UpdateLoadingScreen (qbool clear, qbool startup)
-{
-       keydest_t       old_key_dest;
-       int                     old_key_consoleactive;
 
-       // don't do anything if not initialized yet
-       if (vid_hidden || cls.state == ca_dedicated)
-               return;
-
-       // limit update rate
-       if (scr_loadingscreen_maxfps.value)
-       {
-               double t = Sys_DirtyTime();
-               if ((t - loadingscreen_lastupdate) < 1.0f/scr_loadingscreen_maxfps.value)
-                       return;
-               loadingscreen_lastupdate = t;
-       }
-
-       // set up the r_texture_gammaramps texture which we need for rendering the loadingscreenpic
-       R_UpdateVariables();
-
-       if(!scr_loadingscreen_background.integer)
-               clear = true;
-       
-       if(loadingscreendone)
-               clear |= loadingscreencleared;
-
-       if(!loadingscreendone)
-       {
-               if(startup && scr_loadingscreen_firstforstartup.integer)
-                       loadingscreenpic_number = 0;
-               else if(scr_loadingscreen_firstforstartup.integer)
-                       if(scr_loadingscreen_count.integer > 1)
-                               loadingscreenpic_number = rand() % (scr_loadingscreen_count.integer - 1) + 1;
-                       else
-                               loadingscreenpic_number = 0;
-               else
-                       loadingscreenpic_number = rand() % (scr_loadingscreen_count.integer > 1 ? scr_loadingscreen_count.integer : 1);
-       }
-
-       if(clear)
-               SCR_ClearLoadingScreenTexture();
-       else if(!loadingscreendone)
-               SCR_SetLoadingScreenTexture();
-
-       if(!loadingscreendone)
-       {
-               loadingscreendone = true;
-               loadingscreenheight = 0;
-       }
-       loadingscreencleared = clear;
-
-#ifdef USE_GLES2
-       SCR_DrawLoadingScreen_SharedSetup(clear);
-       SCR_DrawLoadingScreen();
-#else
-       SCR_DrawLoadingScreen_SharedSetup(clear);
-       if (vid.stereobuffer)
-       {
-               qglDrawBuffer(GL_BACK_LEFT);
-               SCR_DrawLoadingScreen();
-               qglDrawBuffer(GL_BACK_RIGHT);
-               SCR_DrawLoadingScreen();
-       }
-       else
-       {
-               qglDrawBuffer(GL_BACK);
-               SCR_DrawLoadingScreen();
-       }
-#endif
-
-       DrawQ_Finish();
-       R_Mesh_Finish();
-       // refresh
-       VID_Finish();
-
-       // this goes into the event loop, and should prevent unresponsive cursor on vista
-       old_key_dest = key_dest;
-       old_key_consoleactive = key_consoleactive;
-       key_dest = key_void;
-       key_consoleactive = false;
-       Key_EventQueue_Block(); Sys_SendKeyEvents();
-       key_dest = old_key_dest;
-       key_consoleactive = old_key_consoleactive;
+       SCR_DrawLoadingStack();
 }
 
 qbool R_Stereo_ColorMasking(void)
@@ -2158,7 +2108,7 @@ qbool R_Stereo_Active(void)
        return (vid.stereobuffer || r_stereo_sidebyside.integer || r_stereo_horizontal.integer || r_stereo_vertical.integer || R_Stereo_ColorMasking());
 }
 
-void SCR_UpdateVars(void)
+static void SCR_UpdateVars(void)
 {
        float conwidth = bound(160, vid_conwidth.value, 32768);
        float conheight = bound(90, vid_conheight.value, 24576);
@@ -2203,9 +2153,10 @@ extern cvar_t cl_minfps_qualitymultiply;
 extern cvar_t cl_minfps_qualityhysteresis;
 extern cvar_t cl_minfps_qualitystepmax;
 extern cvar_t cl_minfps_force;
-static double cl_updatescreen_quality = 1;
 void CL_UpdateScreen(void)
 {
+       static double cl_updatescreen_quality = 1;
+
        vec3_t vieworigin;
        static double drawscreenstart = 0.0;
        double drawscreendelta;
@@ -2294,8 +2245,6 @@ void CL_UpdateScreen(void)
        if (!scr_initialized || !con_initialized || !scr_refresh.integer)
                return;                         // not initialized yet
 
-       loadingscreendone = false;
-
        if(IS_NEXUIZ_DERIVED(gamemode))
        {
                // play a bit with the palette (experimental)
@@ -2319,6 +2268,20 @@ void CL_UpdateScreen(void)
                return;
        }
 
+       if (scr_loading)
+       {
+               if(!loadingscreenstack && !cls.connect_trying && (cls.state != ca_connected || cls.signon == SIGNONS))
+                       SCR_EndLoadingPlaque();
+               else if (scr_loadingscreen_maxfps.value > 0)
+               {
+                       static float lastupdate;
+                       float now = Sys_DirtyTime();
+                       if (now - lastupdate < min(1.0f / scr_loadingscreen_maxfps.value, 0.1))
+                               return;
+                       lastupdate = now;
+               }
+       }
+
        SCR_UpdateVars();
 
        R_FrameData_NewFrame();
@@ -2403,18 +2366,6 @@ void CL_UpdateScreen(void)
 #endif
 
        qglFlush(); // ensure that the commands are submitted to the GPU before we do other things
-
-       if (!vid_activewindow)
-               VID_SetMouse(false, false, false);
-       else if (key_consoleactive)
-               VID_SetMouse(vid.fullscreen, false, false);
-       else if (key_dest == key_menu_grabbed)
-               VID_SetMouse(true, vid_mouse.integer && !in_client_mouse && !vid_touchscreen.integer, !vid_touchscreen.integer);
-       else if (key_dest == key_menu)
-               VID_SetMouse(vid.fullscreen, vid_mouse.integer && !in_client_mouse && !vid_touchscreen.integer, !vid_touchscreen.integer);
-       else
-               VID_SetMouse(vid.fullscreen, vid_mouse.integer && !cl.csqc_wantsmousemove && cl_prydoncursor.integer <= 0 && (!cls.demoplayback || cl_demo_mousegrab.integer) && !vid_touchscreen.integer, !vid_touchscreen.integer);
-
        VID_Finish();
 }
 
index aef5a234fae02c7ac165044f220e825b76d37b54..57cfd282a135f2b2c027afda24556d6f455f51e9 100644 (file)
@@ -17,6 +17,8 @@ extern struct cvar_s scr_screenshot_png;
 extern struct cvar_s scr_screenshot_gammaboost;
 extern struct cvar_s scr_screenshot_name;
 
+extern char cl_connect_status[MAX_QPATH];
+
 void CL_Screen_NewMap(void);
 void CL_Screen_Init(void);
 void CL_Screen_Shutdown(void);
index 2804661b2b8c38c437ac6a51a779a27ee89b32e0..2d5cc453b4b703bb8e3b19194667e322ba88aeae 100644 (file)
@@ -214,7 +214,7 @@ static void LoadSubtitles( clvideo_t *video, const char *subtitlesfile )
 
 static clvideo_t* OpenVideo( clvideo_t *video, const char *filename, const char *name, int owner, const char *subtitlesfile )
 {
-       strlcpy(video->filename, filename, sizeof(video->filename));
+       dp_strlcpy(video->filename, filename, sizeof(video->filename));
        dpsnprintf(video->name, sizeof(video->name), CLVIDEOPREFIX "%s", name);
        video->ownertag = owner;
        if( strncmp( name, CLVIDEOPREFIX, sizeof( CLVIDEOPREFIX ) - 1 ) )
index 04bd63489dfe7253df7ebc91b387e1474809aee8..49d8e96e86da312514943a89fd4be4e3c43a38f2 100644 (file)
@@ -151,7 +151,7 @@ void *jam_open(clvideo_t *video, char *filename, const char **errorstring)
        {
                sfx_t* sfx;
                FS_StripExtension(filename, wavename, namelen);
-               strlcat(wavename, ".wav", namelen);
+               dp_strlcat(wavename, ".wav", namelen);
                sfx = S_PrecacheSound(wavename, false, false);
                if (sfx != NULL)
                        s->sndchan = S_StartSound (-1, 0, sfx, vec3_origin, 1.0f, 0);
index 83f4921c35677005afeb608254a0697add198831..b0181be0dd7d71f409aa74ca2bbe0a0abae342f4 100644 (file)
@@ -335,7 +335,7 @@ void *LibAvW_OpenVideo(clvideo_t *video, char *filename, const char **errorstrin
        if (wavename)
        {
                FS_StripExtension(filename, wavename, len-1);
-               strlcat(wavename, ".wav", len);
+               dp_strlcat(wavename, ".wav", len);
                s->sfx = S_PrecacheSound(wavename, false, false);
                s->sndchan = -1;
                Z_Free(wavename);
index fdf1a7881f5b1c9316056c65ff514c93feaaeee5..9523196d85ca49f6369986df12b1abc0f25c6946 100644 (file)
--- a/client.h
+++ b/client.h
@@ -894,7 +894,7 @@ typedef struct client_state_s
        char sound_name[MAX_SOUNDS][MAX_QPATH];
 
        // for display on solo scoreboard
-       char worldmessage[40]; // map title (not related to filename)
+       char worldmessage[MAX_QPATH]; // map title (not related to filename)
        // variants of map name
        char worldbasename[MAX_QPATH]; // %s
        char worldname[MAX_QPATH]; // maps/%s.bsp
@@ -1048,6 +1048,8 @@ typedef struct client_state_s
 
        // time accumulated since an input packet was sent
        float timesincepacket;
+       // how many optimally timed inputs we sent since we received an update from the server
+       uint8_t opt_inputs_since_update;
 
        // movement parameters for client prediction
        unsigned int moveflags;
@@ -1107,7 +1109,6 @@ typedef struct client_state_s
        // csqc stuff:
        // server entity number corresponding to a clientside entity
        unsigned short csqc_server2csqcentitynumber[MAX_EDICTS];
-       qbool csqc_loaded;
        vec3_t csqc_vieworigin;
        vec3_t csqc_viewangles;
        vec3_t csqc_vieworiginfromengine;
@@ -1116,6 +1117,7 @@ typedef struct client_state_s
        qbool csqc_usecsqclistener;
        matrix4x4_t csqc_listenermatrix;
        char csqc_printtextbuf[MAX_INPUTLINE];
+       size_t csqc_printtextbuf_len; ///< strlen
 
        // collision culling data
        world_t world;
@@ -1133,6 +1135,9 @@ typedef struct client_state_s
 
        // used by EntityState5_ReadUpdate
        skeleton_t *engineskeletonobjects;
+
+       // used by underwater sound filter (snd_waterfx)
+       qbool view_underwater;
 }
 client_state_t;
 
@@ -1165,7 +1170,6 @@ extern cvar_t cl_anglespeedkey;
 extern cvar_t cl_autofire;
 
 extern cvar_t cl_shownet;
-extern cvar_t cl_nolerp;
 extern cvar_t cl_nettimesyncfactor;
 extern cvar_t cl_nettimesyncboundmode;
 extern cvar_t cl_nettimesyncboundtolerance;
@@ -1201,6 +1205,8 @@ extern cvar_t cl_prydoncursor_notrace;
 
 extern cvar_t cl_locs_enable;
 
+extern cvar_t cl_areagrid_link_SOLID_NOT;
+
 extern client_state_t cl;
 
 extern void CL_AllocLightFlash (entity_render_t *ent, matrix4x4_t *matrix, float radius, float red, float green, float blue, float decay, float lifetime, char *cubemapname, int style, int shadowenable, vec_t corona, vec_t coronasizescale, vec_t ambientscale, vec_t diffusescale, vec_t specularscale, int flags);
@@ -1364,6 +1370,7 @@ extern model_t cl_meshentitymodels[NUM_MESHENTITIES];
 extern const char *cl_meshentitynames[NUM_MESHENTITIES];
 #define CL_Mesh_Scene() (&cl_meshentitymodels[MESH_SCENE])
 #define CL_Mesh_UI() (&cl_meshentitymodels[MESH_UI])
+void CL_MeshEntities_Init(void);
 void CL_MeshEntities_Scene_Clear(void);
 void CL_MeshEntities_Scene_AddRenderEntity(void);
 void CL_MeshEntities_Scene_FinalizeRenderEntity(void);
index 2a2cefa68a67f07024856b4f513eae87c56bbb55..f3e986ed278ca2a07e3122653cf4cc7455f4b0ed 100644 (file)
@@ -296,7 +296,7 @@ static void VM_CL_traceline (prvm_prog_t *prog)
        move = (int)PRVM_G_FLOAT(OFS_PARM2);
        ent = PRVM_G_EDICT(OFS_PARM3);
 
-       if (VEC_IS_NAN(v1[0]) || VEC_IS_NAN(v1[1]) || VEC_IS_NAN(v1[2]) || VEC_IS_NAN(v2[0]) || VEC_IS_NAN(v2[1]) || VEC_IS_NAN(v2[2]))
+       if (isnan(v1[0]) || isnan(v1[1]) || isnan(v1[2]) || isnan(v2[0]) || isnan(v2[1]) || isnan(v2[2]))
                prog->error_cmd("%s: NAN errors detected in traceline('%f %f %f', '%f %f %f', %i, entity %i)\n", prog->name, v1[0], v1[1], v1[2], v2[0], v2[1], v2[2], move, PRVM_EDICT_TO_PROG(ent));
 
        trace = CL_TraceLine(v1, v2, move, ent, CL_GenericHitSuperContentsMask(ent), 0, 0, collision_extendtracelinelength.value, CL_HitNetworkBrushModels(move), CL_HitNetworkPlayers(move), &svent, true, false);
@@ -336,7 +336,7 @@ static void VM_CL_tracebox (prvm_prog_t *prog)
        move = (int)PRVM_G_FLOAT(OFS_PARM4);
        ent = PRVM_G_EDICT(OFS_PARM5);
 
-       if (VEC_IS_NAN(v1[0]) || VEC_IS_NAN(v1[1]) || VEC_IS_NAN(v1[2]) || VEC_IS_NAN(v2[0]) || VEC_IS_NAN(v2[1]) || VEC_IS_NAN(v2[2]))
+       if (isnan(v1[0]) || isnan(v1[1]) || isnan(v1[2]) || isnan(v2[0]) || isnan(v2[1]) || isnan(v2[2]))
                prog->error_cmd("%s: NAN errors detected in tracebox('%f %f %f', '%f %f %f', '%f %f %f', '%f %f %f', %i, entity %i)\n", prog->name, v1[0], v1[1], v1[2], m1[0], m1[1], m1[2], m2[0], m2[1], m2[2], v2[0], v2[1], v2[2], move, PRVM_EDICT_TO_PROG(ent));
 
        trace = CL_TraceBox(v1, m1, m2, v2, move, ent, CL_GenericHitSuperContentsMask(ent), 0, 0, collision_extendtraceboxlength.value, CL_HitNetworkBrushModels(move), CL_HitNetworkPlayers(move), &svent, true);
@@ -469,7 +469,7 @@ static void VM_CL_findradius (prvm_prog_t *prog)
        else
                chainfield = prog->fieldoffsets.chain;
        if(chainfield < 0)
-               prog->error_cmd("VM_findchain: %s doesnt have the specified chain field !", prog->name);
+               prog->error_cmd("VM_CL_findradius: %s doesnt have the specified chain field !", prog->name);
 
        chain = (prvm_edict_t *)prog->edicts;
 
@@ -519,6 +519,42 @@ static void VM_CL_findradius (prvm_prog_t *prog)
        VM_RETURN_EDICT(chain);
 }
 
+// #566 entity(vector mins, vector maxs) findbox
+// #566 entity(vector mins, vector maxs, .entity tofield) findbox_tofield
+static void VM_CL_findbox (prvm_prog_t *prog)
+{
+       prvm_edict_t *chain;
+       int i, numtouchedicts;
+       static prvm_edict_t *touchedicts[MAX_EDICTS];
+       int chainfield;
+
+       VM_SAFEPARMCOUNTRANGE(2, 3, VM_CL_findbox);
+
+       if(prog->argc == 3)
+               chainfield = PRVM_G_INT(OFS_PARM2);
+       else
+               chainfield = prog->fieldoffsets.chain;
+       if(chainfield < 0)
+               prog->error_cmd("VM_CL_findbox: %s doesnt have the specified chain field !", prog->name);
+
+       chain = (prvm_edict_t *)prog->edicts;
+
+       numtouchedicts = World_EntitiesInBox(&cl.world, PRVM_G_VECTOR(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM1), MAX_EDICTS, touchedicts);
+       if (numtouchedicts > MAX_EDICTS)
+       {
+               // this never happens   //[515]: for what then ?
+               Con_Printf("World_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
+               numtouchedicts = MAX_EDICTS;
+       }
+       for (i = 0; i < numtouchedicts; ++i)
+       {
+               PRVM_EDICTFIELDEDICT(touchedicts[i], chainfield) = PRVM_EDICT_TO_PROG(chain);
+               chain = touchedicts[i];
+       }
+
+       VM_RETURN_EDICT(chain);
+}
+
 // #34 float() droptofloor
 static void VM_CL_droptofloor (prvm_prog_t *prog)
 {
@@ -547,7 +583,12 @@ static void VM_CL_droptofloor (prvm_prog_t *prog)
        VectorCopy(PRVM_clientedictvector(ent, mins), mins);
        VectorCopy(PRVM_clientedictvector(ent, maxs), maxs);
        VectorCopy(PRVM_clientedictvector(ent, origin), end);
-       end[2] -= 256;
+       if (cl.worldmodel->brush.isq3bsp)
+               end[2] -= 4096;
+       else if (cl.worldmodel->brush.isq2bsp)
+               end[2] -= 128;
+       else
+               end[2] -= 256; // Quake, QuakeWorld
 
        trace = CL_TraceBox(start, mins, maxs, end, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value, true, true, NULL, true);
 
@@ -577,7 +618,7 @@ static void VM_CL_lightstyle (prvm_prog_t *prog)
                VM_Warning(prog, "VM_CL_lightstyle >= MAX_LIGHTSTYLES\n");
                return;
        }
-       strlcpy (cl.lightstyle[i].map, c, sizeof (cl.lightstyle[i].map));
+       dp_strlcpy (cl.lightstyle[i].map, c, sizeof (cl.lightstyle[i].map));
        cl.lightstyle[i].map[MAX_STYLESTRING - 1] = 0;
        cl.lightstyle[i].length = (int)strlen(cl.lightstyle[i].map);
 }
@@ -1598,7 +1639,7 @@ void VM_loadfont(prvm_prog_t *prog)
                if (i >= 0 && i < dp_fonts.maxsize)
                {
                        f = &dp_fonts.f[i];
-                       strlcpy(f->title, fontname, sizeof(f->title)); // replace name
+                       dp_strlcpy(f->title, fontname, sizeof(f->title)); // replace name
                }
        }
        if (!f)
@@ -1622,8 +1663,8 @@ void VM_loadfont(prvm_prog_t *prog)
                f->req_face = 0;
                c = cm;
        }
-       if(!c || (c - filelist) > MAX_QPATH)
-               strlcpy(mainfont, filelist, sizeof(mainfont));
+       if(!c || (c - filelist) >= MAX_QPATH)
+               dp_strlcpy(mainfont, filelist, sizeof(mainfont));
        else
        {
                memcpy(mainfont, filelist, c - filelist);
@@ -1648,9 +1689,9 @@ void VM_loadfont(prvm_prog_t *prog)
                        f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index
                        c = cm;
                }
-               if(!c || (c-filelist) > MAX_QPATH)
+               if(!c || (c-filelist) >= MAX_QPATH)
                {
-                       strlcpy(f->fallbacks[i], filelist, sizeof(mainfont));
+                       dp_strlcpy(f->fallbacks[i], filelist, sizeof(mainfont));
                }
                else
                {
@@ -2022,6 +2063,7 @@ static void VM_CL_getstats (prvm_prog_t *prog)
 {
        int i;
        char t[17];
+       size_t t_len;
        VM_SAFEPARMCOUNT(1, VM_CL_getstats);
        i = (int)PRVM_G_FLOAT(OFS_PARM0);
        if(i < 0 || i > MAX_CL_STATS-4)
@@ -2030,8 +2072,8 @@ static void VM_CL_getstats (prvm_prog_t *prog)
                VM_Warning(prog, "VM_CL_getstats: index>MAX_CL_STATS-4 or index<0\n");
                return;
        }
-       strlcpy(t, (char*)&cl.stats[i], sizeof(t));
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, t);
+       t_len = dp_strlcpy(t, (char*)&cl.stats[i], sizeof(t));
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, t, t_len);
 }
 
 //#333 void(entity e, float mdlindex) setmodelindex (EXT_CSQC)
@@ -2333,9 +2375,10 @@ static void VM_CL_runplayerphysics (prvm_prog_t *prog)
 //#348 string(float playernum, string keyname) getplayerkeyvalue (EXT_CSQC)
 static void VM_CL_getplayerkey (prvm_prog_t *prog)
 {
-       int                     i;
-       char            t[128];
-       const char      *c;
+       int i;
+       char t[128];
+       size_t t_len;
+       const char *c;
 
        VM_SAFEPARMCOUNT(2, VM_CL_getplayerkey);
 
@@ -2350,39 +2393,40 @@ static void VM_CL_getplayerkey (prvm_prog_t *prog)
                return;
 
        t[0] = 0;
+       t_len = 0;
 
        if(!strcasecmp(c, "name"))
-               strlcpy(t, cl.scores[i].name, sizeof(t));
+               t_len = dp_strlcpy(t, cl.scores[i].name, sizeof(t));
        else
                if(!strcasecmp(c, "frags"))
-                       dpsnprintf(t, sizeof(t), "%i", cl.scores[i].frags);
+                       t_len = dpsnprintf(t, sizeof(t), "%i", cl.scores[i].frags);
        else
                if(!strcasecmp(c, "ping"))
-                       dpsnprintf(t, sizeof(t), "%i", cl.scores[i].qw_ping);
+                       t_len = dpsnprintf(t, sizeof(t), "%i", cl.scores[i].qw_ping);
        else
                if(!strcasecmp(c, "pl"))
-                       dpsnprintf(t, sizeof(t), "%i", cl.scores[i].qw_packetloss);
+                       t_len = dpsnprintf(t, sizeof(t), "%i", cl.scores[i].qw_packetloss);
        else
                if(!strcasecmp(c, "movementloss"))
-                       dpsnprintf(t, sizeof(t), "%i", cl.scores[i].qw_movementloss);
+                       t_len = dpsnprintf(t, sizeof(t), "%i", cl.scores[i].qw_movementloss);
        else
                if(!strcasecmp(c, "entertime"))
-                       dpsnprintf(t, sizeof(t), "%f", cl.scores[i].qw_entertime);
+                       t_len = dpsnprintf(t, sizeof(t), "%f", cl.scores[i].qw_entertime);
        else
                if(!strcasecmp(c, "colors"))
-                       dpsnprintf(t, sizeof(t), "%i", cl.scores[i].colors);
+                       t_len = dpsnprintf(t, sizeof(t), "%i", cl.scores[i].colors);
        else
                if(!strcasecmp(c, "topcolor"))
-                       dpsnprintf(t, sizeof(t), "%i", cl.scores[i].colors & 0xf0);
+                       t_len = dpsnprintf(t, sizeof(t), "%i", cl.scores[i].colors & 0xf0);
        else
                if(!strcasecmp(c, "bottomcolor"))
-                       dpsnprintf(t, sizeof(t), "%i", (cl.scores[i].colors &15)<<4);
+                       t_len = dpsnprintf(t, sizeof(t), "%i", (cl.scores[i].colors &15)<<4);
        else
                if(!strcasecmp(c, "viewentity"))
-                       dpsnprintf(t, sizeof(t), "%i", i+1);
+                       t_len = dpsnprintf(t, sizeof(t), "%i", i+1);
        if(!t[0])
                return;
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, t);
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, t, t_len);
 }
 
 //#351 void(vector origin, vector forward, vector right, vector up) SetListener (EXT_CSQC)
@@ -2402,71 +2446,74 @@ static void VM_CL_setlistener (prvm_prog_t *prog)
 static void VM_CL_registercmd (prvm_prog_t *prog)
 {
        VM_SAFEPARMCOUNT(1, VM_CL_registercmd);
-       if(!Cmd_Exists(cmd_local, PRVM_G_STRING(OFS_PARM0)))
-               Cmd_AddCommand(CF_CLIENT, PRVM_G_STRING(OFS_PARM0), NULL, "console command created by QuakeC");
+       Cmd_AddCommand(CF_CLIENT, PRVM_G_STRING(OFS_PARM0), NULL, "console command created by QuakeC");
 }
 
-//#360 float() readbyte (EXT_CSQC)
+//#360 float() ReadByte (EXT_CSQC)
 static void VM_CL_ReadByte (prvm_prog_t *prog)
 {
        VM_SAFEPARMCOUNT(0, VM_CL_ReadByte);
        PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadByte(&cl_message);
 }
 
-//#361 float() readchar (EXT_CSQC)
+//#361 float() ReadChar (EXT_CSQC)
 static void VM_CL_ReadChar (prvm_prog_t *prog)
 {
        VM_SAFEPARMCOUNT(0, VM_CL_ReadChar);
        PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadChar(&cl_message);
 }
 
-//#362 float() readshort (EXT_CSQC)
+//#362 float() ReadShort (EXT_CSQC)
 static void VM_CL_ReadShort (prvm_prog_t *prog)
 {
        VM_SAFEPARMCOUNT(0, VM_CL_ReadShort);
        PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadShort(&cl_message);
 }
 
-//#363 float() readlong (EXT_CSQC)
+//#363 float() ReadLong (EXT_CSQC)
 static void VM_CL_ReadLong (prvm_prog_t *prog)
 {
        VM_SAFEPARMCOUNT(0, VM_CL_ReadLong);
        PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadLong(&cl_message);
 }
 
-//#364 float() readcoord (EXT_CSQC)
+//#364 float() ReadCoord (EXT_CSQC)
 static void VM_CL_ReadCoord (prvm_prog_t *prog)
 {
        VM_SAFEPARMCOUNT(0, VM_CL_ReadCoord);
        PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadCoord(&cl_message, cls.protocol);
 }
 
-//#365 float() readangle (EXT_CSQC)
+//#365 float() ReadAngle (EXT_CSQC)
 static void VM_CL_ReadAngle (prvm_prog_t *prog)
 {
        VM_SAFEPARMCOUNT(0, VM_CL_ReadAngle);
        PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadAngle(&cl_message, cls.protocol);
 }
 
-//#366 string() readstring (EXT_CSQC)
+//#366 string() ReadString (EXT_CSQC)
 static void VM_CL_ReadString (prvm_prog_t *prog)
 {
+       size_t cl_readstring_len;
+
        VM_SAFEPARMCOUNT(0, VM_CL_ReadString);
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
+       cl_readstring_len = MSG_ReadString_len(&cl_message, cl_readstring, sizeof(cl_readstring));
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, cl_readstring, cl_readstring_len);
 }
 
-//#367 float() readfloat (EXT_CSQC)
+//#367 float() ReadFloat (EXT_CSQC)
 static void VM_CL_ReadFloat (prvm_prog_t *prog)
 {
        VM_SAFEPARMCOUNT(0, VM_CL_ReadFloat);
        PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadFloat(&cl_message);
 }
 
-//#501 string() readpicture (DP_CSQC_READWRITEPICTURE)
+//#501 string() ReadPicture (DP_CSQC_READWRITEPICTURE)
 extern cvar_t cl_readpicture_force;
 static void VM_CL_ReadPicture (prvm_prog_t *prog)
 {
        const char *name;
+       size_t name_len;
        unsigned char *data;
        unsigned char *buf;
        unsigned short size;
@@ -2475,7 +2522,8 @@ static void VM_CL_ReadPicture (prvm_prog_t *prog)
 
        VM_SAFEPARMCOUNT(0, VM_CL_ReadPicture);
 
-       name = MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
+       name_len = MSG_ReadString_len(&cl_message, cl_readstring, sizeof(cl_readstring));
+       name = cl_readstring;
        size = (unsigned short) MSG_ReadShort(&cl_message);
 
        // check if a texture of that name exists
@@ -2506,7 +2554,7 @@ static void VM_CL_ReadPicture (prvm_prog_t *prog)
                }
        }
 
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, name);
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, name, name_len);
 }
 
 //////////////////////////////////////////////////////////
@@ -2637,8 +2685,6 @@ static void VM_CL_copyentity (prvm_prog_t *prog)
        }
        memcpy(out->fields.fp, in->fields.fp, prog->entityfields * sizeof(prvm_vec_t));
 
-       if (VectorCompare(PRVM_clientedictvector(out, absmin), PRVM_clientedictvector(out, absmax)))
-               return;
        CL_LinkEdict(out);
 }
 
@@ -3371,7 +3417,7 @@ static void VM_CL_gettaginfo (prvm_prog_t *prog)
        Matrix4x4_ToVectors(&tag_localmatrix, forward, left, up, origin);
 
        PRVM_clientglobalfloat(gettaginfo_parent) = parentindex;
-       PRVM_clientglobalstring(gettaginfo_name) = tagname ? PRVM_SetTempString(prog, tagname) : 0;
+       PRVM_clientglobalstring(gettaginfo_name) = tagname ? PRVM_SetTempString(prog, tagname, strlen(tagname)) : 0;
        VectorCopy(forward, PRVM_clientglobalvector(gettaginfo_forward));
        VectorScale(left, -1, PRVM_clientglobalvector(gettaginfo_right));
        VectorCopy(up, PRVM_clientglobalvector(gettaginfo_up));
@@ -4090,7 +4136,7 @@ static void VM_CL_R_PolygonBegin (prvm_prog_t *prog)
        prog->polygonbegin_model = mod;
        if (texname == NULL || texname[0] == 0)
                texname = "$whiteimage";
-       strlcpy(prog->polygonbegin_texname, texname, sizeof(prog->polygonbegin_texname));
+       dp_strlcpy(prog->polygonbegin_texname, texname, sizeof(prog->polygonbegin_texname));
        prog->polygonbegin_drawflags = drawflags;
        prog->polygonbegin_numvertices = 0;
 }
@@ -4455,10 +4501,10 @@ string(string key) serverkey
 */
 static void VM_CL_serverkey(prvm_prog_t *prog)
 {
-       char string[VM_STRINGTEMP_LENGTH];
+       char string[VM_TEMPSTRING_MAXSIZE];
        VM_SAFEPARMCOUNT(1, VM_CL_serverkey);
        InfoString_GetValue(cl.qw_serverinfo, PRVM_G_STRING(OFS_PARM0), string, sizeof(string));
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, string);
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, string, strlen(string));
 }
 
 /*
@@ -4619,7 +4665,7 @@ static void VM_CL_skel_get_bonename(prvm_prog_t *prog)
                return;
        if (bonenum < 0 || bonenum >= skeleton->model->num_bones)
                return;
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, skeleton->model->data_bones[bonenum].name);
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, skeleton->model->data_bones[bonenum].name, strlen(skeleton->model->data_bones[bonenum].name));
 }
 
 // #267 float(float skel, float bonenum) skel_get_boneparent = #267; // (FTE_CSQC_SKELETONOBJECTS) returns parent num for supplied bonenum, 0 if bonenum has no parent or bone does not exist (returned value is always less than bonenum, you can loop on this)
@@ -4982,7 +5028,7 @@ NULL,                                                     // #42 (QUAKE)
 VM_fabs,                                               // #43 float(float f) fabs (QUAKE)
 NULL,                                                  // #44 vector(entity e, float speed) aim (QUAKE)
 VM_cvar,                                               // #45 float(string s) cvar (QUAKE)
-VM_localcmd_local,                             // #46 void(string s) localcmd (QUAKE)
+VM_localcmd,                                   // #46 void(string s) localcmd (QUAKE)
 VM_nextent,                                            // #47 entity(entity e) nextent (QUAKE)
 VM_CL_particle,                                        // #48 void(vector o, vector d, float color, float count) particle (QUAKE)
 VM_changeyaw,                                  // #49 void() ChangeYaw (QUAKE)
@@ -5052,7 +5098,7 @@ VM_fclose,                                                // #111 void(float fhandle) fclose (FRIK_FILE)
 VM_fgets,                                              // #112 string(float fhandle) fgets (FRIK_FILE)
 VM_fputs,                                              // #113 void(float fhandle, string s) fputs (FRIK_FILE)
 VM_strlen,                                             // #114 float(string s) strlen (FRIK_FILE)
-VM_strcat,                                             // #115 string(string s1, string s2, ...) strcat (FRIK_FILE)
+VM_strcat,                                             // #115 string(string s, string...) strcat (FRIK_FILE)
 VM_substring,                                  // #116 string(string s, float start, float length) substring (FRIK_FILE)
 VM_stov,                                               // #117 vector(string) stov (FRIK_FILE)
 VM_strzone,                                            // #118 string(string s) strzone (FRIK_FILE)
@@ -5299,14 +5345,14 @@ VM_findfont,                                    // #356 float(string fontname) loadfont (DP_GFX_FONTS)
 VM_loadfont,                                   // #357 float(string fontname, string fontmaps, string sizes, float slot) loadfont (DP_GFX_FONTS)
 VM_CL_loadcubemap,                             // #358 void(string cubemapname) loadcubemap (DP_GFX_)
 NULL,                                                  // #359
-VM_CL_ReadByte,                                        // #360 float() readbyte (EXT_CSQC)
-VM_CL_ReadChar,                                        // #361 float() readchar (EXT_CSQC)
-VM_CL_ReadShort,                               // #362 float() readshort (EXT_CSQC)
-VM_CL_ReadLong,                                        // #363 float() readlong (EXT_CSQC)
-VM_CL_ReadCoord,                               // #364 float() readcoord (EXT_CSQC)
-VM_CL_ReadAngle,                               // #365 float() readangle (EXT_CSQC)
-VM_CL_ReadString,                              // #366 string() readstring (EXT_CSQC)
-VM_CL_ReadFloat,                               // #367 float() readfloat (EXT_CSQC)
+VM_CL_ReadByte,                                        // #360 float() ReadByte (EXT_CSQC)
+VM_CL_ReadChar,                                        // #361 float() ReadChar (EXT_CSQC)
+VM_CL_ReadShort,                               // #362 float() ReadShort (EXT_CSQC)
+VM_CL_ReadLong,                                        // #363 float() ReadLong (EXT_CSQC)
+VM_CL_ReadCoord,                               // #364 float() ReadCoord (EXT_CSQC)
+VM_CL_ReadAngle,                               // #365 float() ReadAngle (EXT_CSQC)
+VM_CL_ReadString,                              // #366 string() ReadString (EXT_CSQC)
+VM_CL_ReadFloat,                               // #367 float() ReadFloat (EXT_CSQC)
 NULL,                                          // #368
 NULL,                                                  // #369
 NULL,                                                  // #370
@@ -5506,8 +5552,8 @@ NULL,                                                     // #562
 NULL,                                                  // #563
 NULL,                                                  // #564
 NULL,                                                  // #565
-NULL,                                                  // #566
-NULL,                                                  // #567
+VM_CL_findbox,                                 // #566 entity(vector mins, vector maxs) findbox = #566; (DP_QC_FINDBOX)
+VM_nudgeoutofsolid,                            // #567 float(entity ent) nudgeoutofsolid = #567; (DP_QC_NUDGEOUTOFSOLID)
 NULL,                                                  // #568
 NULL,                                                  // #569
 NULL,                                                  // #570
diff --git a/cmd.c b/cmd.c
index e8f168896d963c39ed1c9673e3aaaa2f2f4d5c86..3e35b336b9c3d3914385336289152e111895a95d 100644 (file)
--- a/cmd.c
+++ b/cmd.c
@@ -74,11 +74,13 @@ Cmd_Defer_f
 Cause a command to be executed after a delay.
 ============
 */
-static cmd_input_t *Cbuf_LinkGet(cmd_buf_t *cbuf, cmd_input_t *existing);
+static void Cbuf_ParseText(cmd_state_t *cmd, llist_t *head, cmd_input_t *existing, const char *text, qbool allowpending);
+static void Cbuf_LinkString(cmd_state_t *cmd, llist_t *head, cmd_input_t *existing, const char *text, qbool leavepending, unsigned int cmdsize);
 static void Cmd_Defer_f (cmd_state_t *cmd)
 {
        cmd_input_t *current;
        cmd_buf_t *cbuf = cmd->cbuf;
+       unsigned int cmdsize;
 
        if(Cmd_Argc(cmd) == 1)
        {
@@ -93,25 +95,19 @@ static void Cmd_Defer_f (cmd_state_t *cmd)
        else if(Cmd_Argc(cmd) == 2 && !strcasecmp("clear", Cmd_Argv(cmd, 1)))
        {
                while(!List_Is_Empty(&cbuf->deferred))
+               {
+                       cbuf->size -= List_Entry(cbuf->deferred.next, cmd_input_t, list)->length;
                        List_Move_Tail(cbuf->deferred.next, &cbuf->free);
+               }
        }
-       else if(Cmd_Argc(cmd) == 3)
+       else if(Cmd_Argc(cmd) == 3 && (cmdsize = strlen(Cmd_Argv(cmd, 2))) )
        {
-               const char *text = Cmd_Argv(cmd, 2);
-               current = Cbuf_LinkGet(cbuf, NULL);
-               current->length = strlen(text);
-               current->source = cmd;
-               current->delay = atof(Cmd_Argv(cmd, 1));
+               Cbuf_Lock(cbuf);
 
-               if(current->size < current->length)
-               {
-                       current->text = (char *)Mem_Realloc(cbuf_mempool, current->text, current->length + 1);
-                       current->size = current->length;
-               }
+               Cbuf_LinkString(cmd, &cbuf->deferred, NULL, Cmd_Argv(cmd, 2), false, cmdsize);
+               List_Entry(cbuf->deferred.prev, cmd_input_t, list)->delay = atof(Cmd_Argv(cmd, 1));
 
-               strlcpy(current->text, text, current->length + 1);
-
-               List_Move_Tail(&current->list, &cbuf->deferred);
+               Cbuf_Unlock(cbuf);
        }
        else
        {
@@ -121,230 +117,141 @@ static void Cmd_Defer_f (cmd_state_t *cmd)
        }
 }
 
-/*
-============
-Cmd_Centerprint_f
-
-Print something to the center of the screen using SCR_Centerprint
-============
-*/
-static void Cmd_Centerprint_f (cmd_state_t *cmd)
-{
-       char msg[MAX_INPUTLINE];
-       unsigned int i, c, p;
-       c = Cmd_Argc(cmd);
-       if(c >= 2)
-       {
-               strlcpy(msg, Cmd_Argv(cmd,1), sizeof(msg));
-               for(i = 2; i < c; ++i)
-               {
-                       strlcat(msg, " ", sizeof(msg));
-                       strlcat(msg, Cmd_Argv(cmd, i), sizeof(msg));
-               }
-               c = (unsigned int)strlen(msg);
-               for(p = 0, i = 0; i < c; ++i)
-               {
-                       if(msg[i] == '\\')
-                       {
-                               if(msg[i+1] == 'n')
-                                       msg[p++] = '\n';
-                               else if(msg[i+1] == '\\')
-                                       msg[p++] = '\\';
-                               else {
-                                       msg[p++] = '\\';
-                                       msg[p++] = msg[i+1];
-                               }
-                               ++i;
-                       } else {
-                               msg[p++] = msg[i];
-                       }
-               }
-               msg[p] = '\0';
-               SCR_CenterPrint(msg);
-       }
-}
-
 /*
 =============================================================================
 
                                                COMMAND BUFFER
 
+ * The Quake command-line is super basic. It can be entered in the console
+ * or in config files. A semicolon is used to terminate a command and chain
+ * them together. Otherwise, a newline delineates command input.
+ *
+ * In most engines, the Quake command-line is a simple linear text buffer that
+ * is parsed when it executes. In Darkplaces, we use a linked list of command
+ * input and parse the input on the spot.
+ *
+ * This was done because Darkplaces allows multiple command interpreters on the
+ * same thread. Previously, each interpreter maintained its own buffer and this
+ * caused problems related to execution order, and maintaining a single simple
+ * buffer for all interpreters makes it non-trivial to keep track of which
+ * command should execute on which interpreter.
+
 =============================================================================
 */
 
-static cmd_input_t *Cbuf_LinkGet(cmd_buf_t *cbuf, cmd_input_t *existing)
+/*
+============
+Cbuf_NodeGet
+
+Returns an existing buffer node for appending or reuse, or allocates a new one
+============
+*/
+static cmd_input_t *Cbuf_NodeGet(cmd_buf_t *cbuf, cmd_input_t *existing)
 {
-       cmd_input_t *ret = NULL;
+       cmd_input_t *node;
        if(existing && existing->pending)
-               ret = existing;
+               node = existing;
        else if(!List_Is_Empty(&cbuf->free))
        {
-               ret = List_Entry(cbuf->free.next, cmd_input_t, list);
-               ret->length = 0;
-               ret->pending = false;
+               node = List_Entry(cbuf->free.next, cmd_input_t, list);
+               node->length = node->pending = 0;
+       }
+       else
+       {
+               node = (cmd_input_t *)Mem_Alloc(cbuf_mempool, sizeof(cmd_input_t));
+               node->list.prev = node->list.next = &node->list;
+               node->size = node->length = node->pending = 0;
        }
-       return ret;
-}
-
-static cmd_input_t *Cmd_AllocInputNode(void)
-{
-       cmd_input_t *node = (cmd_input_t *)Mem_Alloc(cbuf_mempool, sizeof(cmd_input_t));
-       node->list.prev = node->list.next = &node->list;
-       node->size = node->length = node->pending = 0;
        return node;
 }
 
+/*
+============
+Cbuf_LinkString
 
-// Cloudwalk FIXME: The entire design of this thing is overly complicated.
-// We could very much safely have one input node per line whether or not
-// the command was terminated. We don't need to split up input nodes per command
-// executed.
-static size_t Cmd_ParseInput (cmd_input_t **output, char **input)
+Copies a command string into a buffer node.
+The input should not be null-terminated, the output will be.
+============
+*/
+static void Cbuf_LinkString(cmd_state_t *cmd, llist_t *head, cmd_input_t *existing, const char *text, qbool leavepending, unsigned int cmdsize)
 {
-       size_t pos, cmdsize = 0, start = 0;
-       qbool command = false, lookahead = false;
-       qbool quotes = false, comment = false;
-       qbool escaped = false;
-
-       /*
-        * The Quake command-line is super basic. It can be entered in the console
-        * or in config files. A semicolon is used to terminate a command and chain
-        * them together. Otherwise, a newline delineates command input.
-        * 
-        * In most engines, the Quake command-line is a simple linear text buffer that
-        * is parsed when it executes. In Darkplaces, we use a linked list of command
-        * input and parse the input on the spot.
-        * 
-        * This was done because Darkplaces allows multiple command interpreters on the
-        * same thread. Previously, each interpreter maintained its own buffer and this
-        * caused problems related to execution order, and maintaining a single simple
-        * buffer for all interpreters makes it non-trivial to keep track of which
-        * command should execute on which interpreter.
-        */
-
-       // Run until command and lookahead are both true, or until we run out of input.
-       for (pos = 0; (*input)[pos]; pos++)
-       {
-               // Look for newlines and semicolons. Ignore semicolons in quotes.
-               switch((*input)[pos])
-               {
-               case '\r':
-               case '\n':
-                       command = false;
-                       comment = false;
-                       break;
-               default: 
-                       if(!comment) // Not a newline so far. Still not a valid command yet.
-                       {
-                               if(!quotes && (*input)[pos] == ';') // Ignore semicolons in quotes.
-                                       command = false;
-                               else if (ISCOMMENT((*input), pos)) // Comments
-                               {
-                                       comment = true;
-                                       command = false;
-                               }
-                               else
-                               {
-                                       command = true;
-                                       if(!lookahead)
-                                       {
-                                               if(!cmdsize)
-                                                       start = pos;
-                                               cmdsize++;
-                                       }
-
-                                       switch((*input)[pos])
-                                       {
-                                       case '"':
-                                               if (!escaped)
-                                                       quotes = !quotes;
-                                               else
-                                                       escaped = false;
-                                               break;
-                                       case '\\':
-                                               if (!escaped && quotes)
-                                                       escaped = true;
-                                               else if (escaped)
-                                                       escaped = false;
-                                               break;
-                                       }
-                               }
-                       }
-               }
-               if(cmdsize && !command)
-                       lookahead = true;
+       cmd_buf_t *cbuf = cmd->cbuf;
+       cmd_input_t *node = Cbuf_NodeGet(cbuf, existing);
+       unsigned int offset = node->length; // > 0 if(pending)
 
-               if(command && lookahead)
-                       break;
+       // node will match existing if its text was pending continuation
+       if(node != existing)
+       {
+               node->source = cmd;
+               List_Move_Tail(&node->list, head);
        }
 
-       if(cmdsize)
+       node->length += cmdsize;
+       if(node->size < node->length)
        {
-               size_t offset = 0;
-
-               if(!*output)
-                       *output = Cmd_AllocInputNode();
-
-               // Append, since this input line hasn't closed yet.
-               if((*output)->pending)
-                       offset = (*output)->length;
-
-               (*output)->length += cmdsize;
-
-               if((*output)->size < (*output)->length)
-               {
-                       (*output)->text = (char *)Mem_Realloc(cbuf_mempool, (*output)->text, (*output)->length + 1);
-                       (*output)->size = (*output)->length;
-               }
-
-               strlcpy(&(*output)->text[offset], &(*input)[start], cmdsize + 1);
-               
-               /*
-                * If we were still looking ahead by the time we broke from the loop, the command input
-                * hasn't terminated yet and we're still expecting more, so keep this node open for appending later.
-                */
-               (*output)->pending = !lookahead;
+               node->text = (char *)Mem_Realloc(cbuf_mempool, node->text, node->length + 1);
+               node->size = node->length;
        }
+       cbuf->size += cmdsize;
 
-       // Set input to its new position. Can be NULL.
-       *input = &(*input)[pos];
-
-       return cmdsize;
+       dp_ustr2stp(&node->text[offset], node->length + 1, text, cmdsize);
+       //Con_Printf("^5Cbuf_LinkString(): %s `^7%s^5`\n", node->pending ? "append" : "new", &node->text[offset]);
+       node->pending = leavepending;
 }
 
-// Cloudwalk: Not happy with this, but it works.
-static void Cbuf_LinkCreate(cmd_state_t *cmd, llist_t *head, cmd_input_t *existing, const char *text)
+/*
+============
+Cbuf_ParseText
+
+Parses text to isolate command strings for linking into the buffer
+separators: \n \r or unquoted and uncommented ';'
+============
+*/
+static void Cbuf_ParseText(cmd_state_t *cmd, llist_t *head, cmd_input_t *existing, const char *text, qbool allowpending)
 {
-       char *in = (char *)&text[0];
-       cmd_buf_t *cbuf = cmd->cbuf;
-       size_t totalsize = 0, newsize = 0;
-       cmd_input_t *current = NULL;
+       unsigned int cmdsize = 0, start = 0, pos;
+       qbool quotes = false, comment = false;
 
-       // Slide the pointer down until we reach the end
-       while(*in)
+       for (pos = 0; text[pos]; ++pos)
        {
-               // Check if the current node is still accepting input (input line hasn't terminated)
-               current = Cbuf_LinkGet(cbuf, existing);
-               newsize = Cmd_ParseInput(&current, &in);
-
-               // Valid command
-               if(newsize)
+               switch(text[pos])
                {
-                       // current will match existing if the input line hasn't terminated yet
-                       if(current != existing)
-                       {
-                               current->source = cmd;
-                               List_Move_Tail(&current->list, head);
-                       }
+                       case ';':
+                               if (comment || quotes)
+                                       break;
+                       case '\r':
+                       case '\n':
+                               comment = false;
+                               quotes = false; // matches div0-stable
+                               if (cmdsize)
+                               {
+                                       Cbuf_LinkString(cmd, head, existing, &text[start], false, cmdsize);
+                                       cmdsize = 0;
+                               }
+                               else if (existing && existing->pending) // all I got was this lousy \n
+                                       existing->pending = false;
+                               continue; // don't increment cmdsize
+
+                       case '/':
+                               if (!quotes && text[pos + 1] == '/' && (pos == 0 || ISWHITESPACE(text[pos - 1])))
+                                       comment = true;
+                               break;
+                       case '"':
+                               if (!comment && (pos == 0 || text[pos - 1] != '\\'))
+                                       quotes = !quotes;
+                               break;
+               }
 
-                       totalsize += newsize;
+               if (!comment)
+               {
+                       if (!cmdsize)
+                               start = pos;
+                       ++cmdsize;
                }
-               else if (current == existing && !totalsize)
-                       current->pending = false;
-               current = NULL;
        }
 
-       cbuf->size += totalsize;
+       if (cmdsize) // the line didn't end yet but we do have a string
+               Cbuf_LinkString(cmd, head, existing, &text[start], allowpending, cmdsize);
 }
 
 /*
@@ -360,16 +267,18 @@ void Cbuf_AddText (cmd_state_t *cmd, const char *text)
        cmd_buf_t *cbuf = cmd->cbuf;
        llist_t llist = {&llist, &llist};
 
-       Cbuf_Lock(cbuf);
-
-       if (cbuf->maxsize - cbuf->size <= l)
-               Con_Print("Cbuf_AddText: overflow\n");
-       else
+       if (cbuf->size + l > cbuf->maxsize)
        {
-               Cbuf_LinkCreate(cmd, &llist, (List_Is_Empty(&cbuf->start) ? NULL : List_Entry(cbuf->start.prev, cmd_input_t, list)), text);
-               if(!List_Is_Empty(&llist))
-                       List_Splice_Tail(&llist, &cbuf->start);
+               Con_Printf(CON_WARN "Cbuf_AddText: input too large, %luKB ought to be enough for anybody.\n", (unsigned long)(cbuf->maxsize / 1024));
+               return;
        }
+
+       Cbuf_Lock(cbuf);
+
+       // If the string terminates but the (last) line doesn't, the node will be left in the pending state (to be continued).
+       Cbuf_ParseText(cmd, &llist, (List_Is_Empty(&cbuf->start) ? NULL : List_Entry(cbuf->start.prev, cmd_input_t, list)), text, true);
+       List_Splice_Tail(&llist, &cbuf->start);
+
        Cbuf_Unlock(cbuf);
 }
 
@@ -378,7 +287,6 @@ void Cbuf_AddText (cmd_state_t *cmd, const char *text)
 Cbuf_InsertText
 
 Adds command text immediately after the current command
-FIXME: actually change the command buffer to do less copying
 ============
 */
 void Cbuf_InsertText (cmd_state_t *cmd, const char *text)
@@ -387,18 +295,20 @@ void Cbuf_InsertText (cmd_state_t *cmd, const char *text)
        llist_t llist = {&llist, &llist};
        size_t l = strlen(text);
 
-       Cbuf_Lock(cbuf);
-
-       // we need to memmove the existing text and stuff this in before it...
-       if (cbuf->size + l >= cbuf->maxsize)
-               Con_Print("Cbuf_InsertText: overflow\n");
-       else
+       if (cbuf->size + l > cbuf->maxsize)
        {
-               Cbuf_LinkCreate(cmd, &llist, List_Entry(cbuf->start.next, cmd_input_t, list), text);
-               if(!List_Is_Empty(&llist))
-                       List_Splice(&llist, &cbuf->start);
+               Con_Printf(CON_WARN "Cbuf_InsertText: input too large, %luKB ought to be enough for anybody.\n", (unsigned long)(cbuf->maxsize / 1024));
+               return;
        }
 
+       Cbuf_Lock(cbuf);
+
+       // bones_was_here assertion: when prepending to the buffer it never makes sense to leave node(s) in the `pending` state,
+       // it would have been impossible to append to such text later in the old raw text buffer,
+       // and allowing it causes bugs when .cfg files lack \n at EOF (see: https://gitlab.com/xonotic/darkplaces/-/issues/378).
+       Cbuf_ParseText(cmd, &llist, (List_Is_Empty(&cbuf->start) ? NULL : List_Entry(cbuf->start.next, cmd_input_t, list)), text, false);
+       List_Splice(&llist, &cbuf->start);
+
        Cbuf_Unlock(cbuf);
 }
 
@@ -409,25 +319,25 @@ Cbuf_Execute_Deferred --blub
 */
 static void Cbuf_Execute_Deferred (cmd_buf_t *cbuf)
 {
-       cmd_input_t *current;
-       double eat;
+       cmd_input_t *current, *n;
+       vec_t eat;
 
        if (host.realtime - cbuf->deferred_oldtime < 0 || host.realtime - cbuf->deferred_oldtime > 1800)
                cbuf->deferred_oldtime = host.realtime;
        eat = host.realtime - cbuf->deferred_oldtime;
-       if (eat < (1.0 / 120.0))
+       if (eat < 1.0/128.0)
                return;
        cbuf->deferred_oldtime = host.realtime;
 
-       List_For_Each_Entry(current, &cbuf->deferred, cmd_input_t, list)
+       List_For_Each_Entry_Safe(current, n, &cbuf->deferred, cmd_input_t, list)
        {
                current->delay -= eat;
                if(current->delay <= 0)
                {
-                       cbuf->size += current->length;
-                       List_Move(&current->list, &cbuf->start);
-                       // We must return and come back next frame or the engine will freeze. Fragile... like glass :3
-                       return;
+                       Cbuf_AddText(current->source, current->text); // parse deferred string and append its cmdstring(s)
+                       List_Entry(cbuf->start.prev, cmd_input_t, list)->pending = false; // faster than div0-stable's Cbuf_AddText(";\n");
+                       List_Move_Tail(&current->list, &cbuf->free); // make deferred string memory available for reuse
+                       cbuf->size -= current->length;
                }
        }
 }
@@ -437,12 +347,11 @@ static void Cbuf_Execute_Deferred (cmd_buf_t *cbuf)
 Cbuf_Execute
 ============
 */
-static qbool Cmd_PreprocessString(cmd_state_t *cmd, const char *intext, char *outtext, unsigned maxoutlen, cmd_alias_t *alias );
+extern qbool prvm_runawaycheck;
 void Cbuf_Execute (cmd_buf_t *cbuf)
 {
        cmd_input_t *current;
-       char preprocessed[MAX_INPUTLINE];
-       char *firstchar;
+       unsigned int i = 0;
 
        // LadyHavoc: making sure the tokenizebuffer doesn't get filled up by repeated crashes
        cbuf->tokenizebufferpos = 0;
@@ -455,10 +364,7 @@ void Cbuf_Execute (cmd_buf_t *cbuf)
                 * can insert data at the beginning of the text buffer
                 */
                current = List_Entry(cbuf->start.next, cmd_input_t, list);
-               
-               // Recycle memory so using WASD doesn't cause a malloc and free
-               List_Move_Tail(&current->list, &cbuf->free);
-               
+
                /*
                 * Assume we're rolling with the current command-line and
                 * always set this false because alias expansion or cbuf insertion
@@ -466,24 +372,10 @@ void Cbuf_Execute (cmd_buf_t *cbuf)
                 */
                current->pending = false;
 
+               Cmd_PreprocessAndExecuteString(current->source, current->text, current->length, src_local, false);
                cbuf->size -= current->length;
-
-               firstchar = current->text;
-               while(*firstchar && ISWHITESPACE(*firstchar))
-                       ++firstchar;
-               if((strncmp(firstchar, "alias", 5)   || !ISWHITESPACE(firstchar[5])) &&
-                  (strncmp(firstchar, "bind", 4)    || !ISWHITESPACE(firstchar[4])) &&
-                  (strncmp(firstchar, "in_bind", 7) || !ISWHITESPACE(firstchar[7])))
-               {
-                       if(Cmd_PreprocessString(current->source, current->text, preprocessed, sizeof(preprocessed), NULL ))
-                               Cmd_ExecuteString(current->source, preprocessed, src_local, false);
-               }
-               else
-               {
-                       Cmd_ExecuteString (current->source, current->text, src_local, false);
-               }
-
-               current = NULL;
+               // Recycle memory so using WASD doesn't cause a malloc and free
+               List_Move_Tail(&current->list, &cbuf->free);
 
                if (cbuf->wait)
                {
@@ -494,6 +386,12 @@ void Cbuf_Execute (cmd_buf_t *cbuf)
                        cbuf->wait = false;
                        break;
                }
+
+               if (++i == 1000000 && prvm_runawaycheck)
+               {
+                       Con_Printf(CON_WARN "Cbuf_Execute: runaway loop counter hit limit of %d commands, clearing command buffers!\n", i);
+                       Cbuf_Clear(cbuf);
+               }
        }
 }
 
@@ -508,8 +406,12 @@ static void Cbuf_Frame_Input(void)
 {
        char *line;
 
-       while ((line = Sys_ConsoleInput()))
-                       Cbuf_AddText(cmd_local, line);
+       if ((line = Sys_ConsoleInput()))
+       {
+               // bones_was_here: prepending allows a loop such as `alias foo "bar; wait; foo"; foo`
+               // to be broken with an alias or unalias command
+               Cbuf_InsertText(cmd_local, line);
+       }
 }
 
 void Cbuf_Frame(cmd_buf_t *cbuf)
@@ -531,6 +433,15 @@ void Cbuf_Frame(cmd_buf_t *cbuf)
 //     R_TimeReport("console");
 }
 
+void Cbuf_Clear(cmd_buf_t *cbuf)
+{
+       while (!List_Is_Empty(&cbuf->start))
+               List_Move_Tail(cbuf->start.next, &cbuf->free);
+       while (!List_Is_Empty(&cbuf->deferred))
+               List_Move_Tail(cbuf->deferred.next, &cbuf->free);
+       cbuf->size = 0;
+}
+
 /*
 ==============================================================================
 
@@ -620,7 +531,7 @@ static void Cmd_Exec(cmd_state_t *cmd, const char *filename)
        f = (char *)FS_LoadFile (filename, tempmempool, false, NULL);
        if (!f)
        {
-               Con_Printf("couldn't exec %s\n",filename);
+               Con_Printf(CON_WARN "couldn't exec %s\n",filename);
                return;
        }
        Con_Printf("execing %s\n",filename);
@@ -803,7 +714,8 @@ static void Cmd_Exec(cmd_state_t *cmd, const char *filename)
                        Cbuf_InsertText(cmd, "\n"
 "csqc_polygons_defaultmaterial_nocullface 1\n"
 "con_chatsound_team_mask 13\n"
-"sv_gameplayfix_customstats 1\n"
+"sv_qcstats 1\n"
+"mod_q1bsp_zero_hullsize_cutoff 8.03125\n"
                                );
                        break;
                // Steel Storm: Burning Retribution csqc misinterprets CSQC_InputEvent if type is a value other than 0 or 1
@@ -877,7 +789,7 @@ static void Cmd_Exec_f (cmd_state_t *cmd)
        s = FS_Search(Cmd_Argv(cmd, 1), true, true, NULL);
        if(!s || !s->numfilenames)
        {
-               Con_Printf("couldn't exec %s\n",Cmd_Argv(cmd, 1));
+               Con_Printf(CON_WARN "couldn't exec %s\n",Cmd_Argv(cmd, 1));
                return;
        }
 
@@ -979,7 +891,7 @@ static void Cmd_Toggle_f(cmd_state_t *cmd)
                }
                else
                { // Invalid CVar
-                       Con_Printf("ERROR : CVar '%s' not found\n", Cmd_Argv(cmd, 1) );
+                       Con_Printf(CON_WARN "ERROR : CVar '%s' not found\n", Cmd_Argv(cmd, 1) );
                }
        }
 }
@@ -1010,7 +922,7 @@ static void Cmd_Alias_f (cmd_state_t *cmd)
        s = Cmd_Argv(cmd, 1);
        if (strlen(s) >= MAX_ALIAS_NAME)
        {
-               Con_Print("Alias name is too long\n");
+               Con_Print(CON_WARN "Alias name is too long\n");
                return;
        }
 
@@ -1029,7 +941,7 @@ static void Cmd_Alias_f (cmd_state_t *cmd)
                cmd_alias_t *prev, *current;
 
                a = (cmd_alias_t *)Z_Malloc (sizeof(cmd_alias_t));
-               strlcpy (a->name, s, sizeof (a->name));
+               dp_strlcpy (a->name, s, sizeof (a->name));
                // insert it at the right alphanumeric position
                for( prev = NULL, current = cmd->userdefined->alias ; current && strcmp( current->name, a->name ) < 0 ; prev = current, current = current->next )
                        ;
@@ -1048,10 +960,10 @@ static void Cmd_Alias_f (cmd_state_t *cmd)
        for (i=2 ; i < c ; i++)
        {
                if (i != 2)
-                       strlcat (line, " ", sizeof (line));
-               strlcat (line, Cmd_Argv(cmd, i), sizeof (line));
+                       dp_strlcat (line, " ", sizeof (line));
+               dp_strlcat (line, Cmd_Argv(cmd, i), sizeof (line));
        }
-       strlcat (line, "\n", sizeof (line));
+       dp_strlcat (line, "\n", sizeof (line));
 
        alloclen = strlen (line) + 1;
        if(alloclen >= 2)
@@ -1348,12 +1260,14 @@ static const char *Cmd_GetCvarValue(cmd_state_t *cmd, const char *var, size_t va
        return varstr;
 }
 
-/*
+/**
 Cmd_PreprocessString
 
 Preprocesses strings and replaces $*, $param#, $cvar accordingly. Also strips comments.
+Returns the number of bytes written to *outtext excluding the \0 terminator.
 */
-static qbool Cmd_PreprocessString(cmd_state_t *cmd, const char *intext, char *outtext, unsigned maxoutlen, cmd_alias_t *alias ) {
+static size_t Cmd_PreprocessString(cmd_state_t *cmd, const char *intext, char *outtext, unsigned maxoutlen, cmd_alias_t *alias)
+{
        const char *in;
        size_t eat, varlen;
        unsigned outlen;
@@ -1361,7 +1275,7 @@ static qbool Cmd_PreprocessString(cmd_state_t *cmd, const char *intext, char *ou
 
        // don't crash if there's no room in the outtext buffer
        if( maxoutlen == 0 ) {
-               return false;
+               return 0;
        }
        maxoutlen--; // because of \0
 
@@ -1434,7 +1348,7 @@ static qbool Cmd_PreprocessString(cmd_state_t *cmd, const char *intext, char *ou
                                {
                                        val = Cmd_GetCvarValue(cmd, in + 1, varlen, alias);
                                        if(!val)
-                                               return false;
+                                               return 0;
                                        eat = varlen + 2;
                                }
                                else
@@ -1447,7 +1361,7 @@ static qbool Cmd_PreprocessString(cmd_state_t *cmd, const char *intext, char *ou
                                varlen = strspn(in, "#*0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-");
                                val = Cmd_GetCvarValue(cmd, in, varlen, alias);
                                if(!val)
-                                       return false;
+                                       return 0;
                                eat = varlen;
                        }
                        if(val)
@@ -1471,8 +1385,8 @@ static qbool Cmd_PreprocessString(cmd_state_t *cmd, const char *intext, char *ou
                else 
                        outtext[outlen++] = *in++;
        }
-       outtext[outlen] = 0;
-       return true;
+       outtext[outlen] = '\0';
+       return outlen;
 }
 
 /*
@@ -1499,6 +1413,26 @@ static void Cmd_ExecuteAlias (cmd_state_t *cmd, cmd_alias_t *alias)
        Cbuf_InsertText(cmd, buffer2);
 }
 
+void Cmd_PreprocessAndExecuteString(cmd_state_t *cmd, const char *text, size_t textlen, cmd_source_t src, qbool lockmutex)
+{
+       char preprocessed[MAX_INPUTLINE];
+       size_t preprocessed_len;
+       const char *firstchar;
+
+       firstchar = text;
+       while(*firstchar && ISWHITESPACE(*firstchar))
+               ++firstchar;
+       if((strncmp(firstchar, "alias", 5)   || !ISWHITESPACE(firstchar[5]))
+       && (strncmp(firstchar, "bind", 4)    || !ISWHITESPACE(firstchar[4]))
+       && (strncmp(firstchar, "in_bind", 7) || !ISWHITESPACE(firstchar[7])))
+       {
+               if((preprocessed_len = Cmd_PreprocessString(cmd, text, preprocessed, sizeof(preprocessed), NULL)))
+                       Cmd_ExecuteString(cmd, preprocessed, preprocessed_len, src, lockmutex);
+       }
+       else
+               Cmd_ExecuteString(cmd, text, textlen, src, lockmutex);
+}
+
 /*
 ========
 Cmd_List
@@ -1626,7 +1560,7 @@ static void Cmd_Apropos_f(cmd_state_t *cmd)
        Con_Printf("%i result%s\n\n", count, (count > 1) ? "s" : "");
 }
 
-static cmd_state_t *Cmd_AddInterpreter(cmd_buf_t *cbuf, cvar_state_t *cvars, int cvars_flagsmask, int cmds_flagsmask, cmd_userdefined_t *userdefined)
+static cmd_state_t *Cmd_AddInterpreter(cmd_buf_t *cbuf, cvar_state_t *cvars, unsigned cvars_flagsmask, unsigned cmds_flagsmask, cmd_userdefined_t *userdefined)
 {
        cmd_state_t *cmd = (cmd_state_t *)Mem_Alloc(tempmempool, sizeof(cmd_state_t));
        
@@ -1637,7 +1571,7 @@ static cmd_state_t *Cmd_AddInterpreter(cmd_buf_t *cbuf, cvar_state_t *cvars, int
 
        cmd->cvars = cvars;
        cmd->cvars_flagsmask = cvars_flagsmask;
-       cmd->cmd_flags = cmds_flagsmask;
+       cmd->cmd_flagsmask = cmds_flagsmask;
        cmd->userdefined = userdefined;
 
        return cmd;
@@ -1651,9 +1585,11 @@ Cmd_Init
 void Cmd_Init(void)
 {
        cmd_buf_t *cbuf;
+       unsigned cvars_flagsmask, cmds_flagsmask;
+
        cbuf_mempool = Mem_AllocPool("Command buffer", 0, NULL);
        cbuf = (cmd_buf_t *)Mem_Alloc(cbuf_mempool, sizeof(cmd_buf_t));
-       cbuf->maxsize = 655360;
+       cbuf->maxsize = CMDBUFSIZE;
        cbuf->lock = Thread_CreateMutex();
        cbuf->wait = false;
        host.cbuf = cbuf;
@@ -1666,14 +1602,22 @@ void Cmd_Init(void)
        cmd_iter_all = (cmd_iter_t *)Mem_Alloc(tempmempool, sizeof(cmd_iter_t) * 3);
 
        // local console
-       cmd_iter_all[0].cmd = cmd_local = Cmd_AddInterpreter(cbuf, &cvars_all, CF_CLIENT | CF_SERVER, CF_CLIENT | CF_CLIENT_FROM_SERVER | CF_SERVER_FROM_CLIENT, &cmd_userdefined_all);
+       if (cls.state == ca_dedicated)
+       {
+               cvars_flagsmask = CF_SERVER;
+               cmds_flagsmask = CF_SERVER | CF_SERVER_FROM_CLIENT;
+       }
+       else
+       {
+               cvars_flagsmask = CF_CLIENT | CF_SERVER;
+               cmds_flagsmask = CF_CLIENT | CF_SERVER | CF_CLIENT_FROM_SERVER | CF_SERVER_FROM_CLIENT;
+       }
+       cmd_iter_all[0].cmd = cmd_local = Cmd_AddInterpreter(cbuf, &cvars_all, cvars_flagsmask, cmds_flagsmask, &cmd_userdefined_all);
        cmd_local->Handle = Cmd_CL_Callback;
-       cmd_local->NotFound = NULL;
 
        // server commands received from clients have no reason to access cvars, cvar expansion seems perilous.
        cmd_iter_all[1].cmd = cmd_serverfromclient = Cmd_AddInterpreter(cbuf, &cvars_null, 0, CF_SERVER_FROM_CLIENT | CF_USERINFO, &cmd_userdefined_null);
        cmd_serverfromclient->Handle = Cmd_SV_Callback;
-       cmd_serverfromclient->NotFound = Cmd_SV_NotFound;
 
        cmd_iter_all[2].cmd = NULL;
 //
@@ -1681,7 +1625,6 @@ void Cmd_Init(void)
 //
        // client-only commands
        Cmd_AddCommand(CF_SHARED, "wait", Cmd_Wait_f, "make script execution wait for next rendered frame");
-       Cmd_AddCommand(CF_CLIENT, "cprint", Cmd_Centerprint_f, "print something at the screen center");
 
        // maintenance commands used for upkeep of cvars and saved configs
        Cmd_AddCommand(CF_SHARED, "stuffcmds", Cmd_StuffCmds_f, "execute commandline parameters (must be present in quake.rc script)");
@@ -1739,46 +1682,15 @@ void Cmd_Shutdown(void)
        }
 }
 
-/*
-============
-Cmd_Argc
-============
-*/
-int            Cmd_Argc (cmd_state_t *cmd)
-{
-       return cmd->argc;
-}
-
-/*
-============
-Cmd_Argv
-============
-*/
-const char *Cmd_Argv(cmd_state_t *cmd, int arg)
-{
-       if (arg >= cmd->argc )
-               return cmd->null_string;
-       return cmd->argv[arg];
-}
-
-/*
-============
-Cmd_Args
-============
-*/
-const char *Cmd_Args (cmd_state_t *cmd)
-{
-       return cmd->args;
-}
-
 /*
 ============
 Cmd_TokenizeString
 
 Parses the given string into command line tokens.
+Takes a null terminated string.  Does not need to be /n terminated.
 ============
 */
-// AK: This function should only be called from ExcuteString because the current design is a bit of an hack
+// AK: This function should only be called from ExecuteString because the current design is a bit of an hack
 static void Cmd_TokenizeString (cmd_state_t *cmd, const char *text)
 {
        int l;
@@ -1822,7 +1734,7 @@ static void Cmd_TokenizeString (cmd_state_t *cmd, const char *text)
                        l = (int)strlen(com_token) + 1;
                        if (cmd->cbuf->tokenizebufferpos + l > CMD_TOKENIZELENGTH)
                        {
-                               Con_Printf("Cmd_TokenizeString: ran out of %i character buffer space for command arguments\n", CMD_TOKENIZELENGTH);
+                               Con_Printf(CON_WARN "Cmd_TokenizeString: ran out of %i character buffer space for command arguments\n", CMD_TOKENIZELENGTH);
                                break;
                        }
                        memcpy (cmd->cbuf->tokenizebuffer + cmd->cbuf->tokenizebufferpos, com_token, l);
@@ -1839,7 +1751,7 @@ static void Cmd_TokenizeString (cmd_state_t *cmd, const char *text)
 Cmd_AddCommand
 ============
 */
-void Cmd_AddCommand(int flags, const char *cmd_name, xcommand_t function, const char *description)
+void Cmd_AddCommand(unsigned flags, const char *cmd_name, xcommand_t function, const char *description)
 {
        cmd_function_t *func;
        cmd_function_t *prev, *current;
@@ -1849,12 +1761,12 @@ void Cmd_AddCommand(int flags, const char *cmd_name, xcommand_t function, const
        for (i = 0; i < 2; i++)
        {
                cmd = cmd_iter_all[i].cmd;
-               if (flags & cmd->cmd_flags)
+               if (flags & cmd->cmd_flagsmask)
                {
                        // fail if the command is a variable name
                        if (Cvar_FindVar(cmd->cvars, cmd_name, ~0))
                        {
-                               Con_Printf("Cmd_AddCommand: %s already defined as a var\n", cmd_name);
+                               Con_Printf(CON_WARN "Cmd_AddCommand: %s already defined as a var\n", cmd_name);
                                return;
                        }
 
@@ -1865,7 +1777,7 @@ void Cmd_AddCommand(int flags, const char *cmd_name, xcommand_t function, const
                                {
                                        if (!strcmp(cmd_name, func->name))
                                        {
-                                               Con_Printf("Cmd_AddCommand: %s already defined\n", cmd_name);
+                                               Con_Printf(CON_WARN "Cmd_AddCommand: %s already defined\n", cmd_name);
                                                continue;
                                        }
                                }
@@ -1900,7 +1812,6 @@ void Cmd_AddCommand(int flags, const char *cmd_name, xcommand_t function, const
                                        }
                                }
 
-
                                func = (cmd_function_t *)Mem_Alloc(cmd->mempool, sizeof(cmd_function_t));
                                func->flags = flags;
                                func->name = cmd_name;
@@ -1909,6 +1820,18 @@ void Cmd_AddCommand(int flags, const char *cmd_name, xcommand_t function, const
                                func->qcfunc = true; //[515]: csqc
                                func->next = cmd->userdefined->qc_functions;
 
+                               // bones_was_here: if this QC command overrides an engine command, store its pointer
+                               // to avoid doing this search at invocation if QC declines to handle this command.
+                               for (cmd_function_t *f = cmd->engine_functions; f; f = f->next)
+                               {
+                                       if (!strcmp(cmd_name, f->name))
+                                       {
+                                               Con_DPrintf("Adding QC override of engine command %s\n", cmd_name);
+                                               func->overridden = f;
+                                               break;
+                                       }
+                               }
+
                                // insert it at the right alphanumeric position
                                for (prev = NULL, current = cmd->userdefined->qc_functions; current && strcmp(current->name, func->name) < 0; prev = current, current = current->next)
                                        ;
@@ -2175,23 +2098,26 @@ extern cvar_t sv_cheats;
  * implement that behavior that doesn't involve an #ifdef, or
  * making a mess of hooks.
  */
-qbool Cmd_Callback(cmd_state_t *cmd, cmd_function_t *func, const char *text, cmd_source_t src)
+qbool Cmd_Callback(cmd_state_t *cmd, cmd_function_t *func)
 {
        if (func->function)
                func->function(cmd);
        else
-               Con_Printf("Command \"%s\" can not be executed\n", Cmd_Argv(cmd, 0));
+               Con_Printf(CON_WARN "Command \"%s\" can not be executed\n", Cmd_Argv(cmd, 0));
        return true;
 }
 
-qbool Cmd_CL_Callback(cmd_state_t *cmd, cmd_function_t *func, const char *text, cmd_source_t src)
+qbool Cmd_CL_Callback(cmd_state_t *cmd, cmd_function_t *func, const char *text, size_t textlen, cmd_source_t src)
 {
        // TODO: Assign these functions to QC commands directly?
        if(func->qcfunc)
        {
-               if(((func->flags & CF_CLIENT) && CL_VM_ConsoleCommand(text)) ||
-                  ((func->flags & CF_SERVER) && SV_VM_ConsoleCommand(text)))
+               if(((func->flags & CF_CLIENT) && CL_VM_ConsoleCommand(text, textlen)) ||
+                  ((func->flags & CF_SERVER) && SV_VM_ConsoleCommand(text, textlen)))
                        return true;
+
+               if (func->overridden) // If this QC command overrides an engine command,
+                       func = func->overridden; // fall back to that command.
        }
        if (func->flags & CF_SERVER_FROM_CLIENT)
        {
@@ -2202,21 +2128,21 @@ qbool Cmd_CL_Callback(cmd_state_t *cmd, cmd_function_t *func, const char *text,
                }
                else if(!(func->flags & CF_SERVER))
                {
-                       Con_Printf("Cannot execute client commands from a dedicated server console.\n");
+                       Con_Printf(CON_WARN "Cannot execute client commands from a dedicated server console.\n");
                        return true;
                }
        }
-       return Cmd_Callback(cmd, func, text, src);
+       return Cmd_Callback(cmd, func);
 }
 
-qbool Cmd_SV_Callback(cmd_state_t *cmd, cmd_function_t *func, const char *text, cmd_source_t src)
+qbool Cmd_SV_Callback(cmd_state_t *cmd, cmd_function_t *func, const char *text, size_t textlen, cmd_source_t src)
 {
        if(func->qcfunc && (func->flags & CF_SERVER))
-               return SV_VM_ConsoleCommand(text);
+               return SV_VM_ConsoleCommand(text, textlen);
        else if (src == src_client)
        {
                if((func->flags & CF_CHEAT) && !sv_cheats.integer)
-                       SV_ClientPrintf("No cheats allowed. The server must have sv_cheats set to 1\n");
+                       SV_ClientPrintf(CON_WARN "No cheats allowed. The server must have sv_cheats set to 1\n");
                else
                        func->function(cmd);
                return true;
@@ -2224,15 +2150,6 @@ qbool Cmd_SV_Callback(cmd_state_t *cmd, cmd_function_t *func, const char *text,
        return false;
 }
 
-qbool Cmd_SV_NotFound(cmd_state_t *cmd, cmd_function_t *func, const char *text, cmd_source_t src)
-{
-       if (cmd->source == src_client)
-       {
-               Con_Printf("Client \"%s\" tried to execute \"%s\"\n", host_client->name, text);
-               return true;
-       }
-       return false;
-}
 /*
 ============
 Cmd_ExecuteString
@@ -2241,11 +2158,12 @@ A complete command line has been parsed, so try to execute it
 FIXME: lookupnoadd the token to speed search?
 ============
 */
-void Cmd_ExecuteString (cmd_state_t *cmd, const char *text, cmd_source_t src, qbool lockmutex)
+void Cmd_ExecuteString(cmd_state_t *cmd, const char *text, size_t textlen, cmd_source_t src, qbool lockmutex)
 {
        int oldpos;
        cmd_function_t *func;
        cmd_alias_t *a;
+
        if (lockmutex)
                Cbuf_Lock(cmd->cbuf);
        oldpos = cmd->cbuf->tokenizebufferpos;
@@ -2259,31 +2177,27 @@ void Cmd_ExecuteString (cmd_state_t *cmd, const char *text, cmd_source_t src, qb
 
 // check functions
        for (func = cmd->userdefined->qc_functions; func; func = func->next)
-       {
                if (!strcasecmp(cmd->argv[0], func->name))
-               {
-                       if(cmd->Handle(cmd, func, text, src))
-                               goto done;
-               }
-       }
+                       if(cmd->Handle(cmd, func, text, textlen, src))
+                               goto functions_done;
 
        for (func = cmd->engine_functions; func; func=func->next)
-       {
                if (!strcasecmp (cmd->argv[0], func->name))
-               {
-                       if(cmd->Handle(cmd, func, text, src))
-                               goto done;
-               }
-       }
+                       if(cmd->Handle(cmd, func, text, textlen, src))
+                               goto functions_done;
 
-       // if it's a client command and no command was found, say so.
-       if(cmd->NotFound)
+functions_done:
+       // If it's a client command and wasn't found and handled, say so.
+       // Also don't let clients call server aliases.
+       if (cmd->source == src_client)
        {
-               if(cmd->NotFound(cmd, func, text, src))
-                       goto done;
+               if (!func)
+                       Con_Printf("Client \"%s\" tried to execute \"%s\"\n", host_client->name, text);
+               goto done;
        }
 
 // check alias
+       // Execute any alias with the same name as a command after the command.
        for (a=cmd->userdefined->alias ; a ; a=a->next)
        {
                if (!strcasecmp (cmd->argv[0], a->name))
@@ -2293,9 +2207,14 @@ void Cmd_ExecuteString (cmd_state_t *cmd, const char *text, cmd_source_t src, qb
                }
        }
 
+       // If the command was found and handled don't try to handle it as a cvar.
+       if (func)
+               goto done;
+
 // check cvars
-       if (!Cvar_Command(cmd) && host.framecount > 0)
-               Con_Printf("Unknown command \"%s\"\n", Cmd_Argv(cmd, 0));
+       // Xonotic is still maintained so we don't want to hide problems from getting fixed
+       if (!Cvar_Command(cmd) && (host.framecount > 0 || gamemode == GAME_XONOTIC))
+               Con_Printf(CON_WARN "Unknown command \"%s\"\n", Cmd_Argv(cmd, 0));
 done:
        cmd->cbuf->tokenizebufferpos = oldpos;
        if (lockmutex)
@@ -2317,7 +2236,7 @@ int Cmd_CheckParm (cmd_state_t *cmd, const char *parm)
 
        if (!parm)
        {
-               Con_Printf ("Cmd_CheckParm: NULL");
+               Con_Printf(CON_WARN "Cmd_CheckParm: NULL");
                return 0;
        }
 
diff --git a/cmd.h b/cmd.h
index c80fdf9078307f31f2f0afe5f9715af6fad25170..a94ddb16665d9fe91623af7fea8a5f5b91e79bbc 100644 (file)
--- a/cmd.h
+++ b/cmd.h
@@ -44,33 +44,35 @@ The game starts with a Cbuf_AddText ("exec quake.rc\n"); Cbuf_Execute ();
 struct cmd_state_s;
 
 // Command flags
-#define CF_NONE 0
-#define CF_CLIENT               (1<<0)  // cvar/command that only the client can change/execute
-#define CF_SERVER               (1<<1)  // cvar/command that only the server can change/execute
-#define CF_CLIENT_FROM_SERVER   (1<<2)  // command that the server is allowed to execute on the client
-#define CF_SERVER_FROM_CLIENT   (1<<3)  // command the client is allowed to execute on the server as a stringcmd
-#define CF_CHEAT                (1<<4)  // command or cvar that gives an unfair advantage over other players and is blocked unless sv_cheats is 1
-#define CF_ARCHIVE              (1<<5)  // cvar should have its set value saved to config.cfg and persist across sessions
-#define CF_READONLY             (1<<6)  // cvar cannot be changed from the console or the command buffer
-#define CF_NOTIFY               (1<<7)  // cvar should trigger a chat notification to all connected clients when changed
-#define CF_SERVERINFO           (1<<8)  // command or cvar relevant to serverinfo string handling
-#define CF_USERINFO             (1<<9)  // command or cvar used to communicate userinfo to the server
-#define CF_PERSISTENT           (1<<10) // cvar must not be reset on gametype switch (such as scr_screenshot_name, which otherwise isn't set to the mod name properly)
-#define CF_PRIVATE              (1<<11) // cvar should not be $ expanded or sent to the server under any circumstances (rcon_password, etc)
-#define CF_MAXFLAGSVAL          ((1<<12) - 1)    // used to determine if flags is valid
+#define CF_NONE 0u
+#define CF_CLIENT               (1u<<0)  ///< cvar/command that only the client can change/execute
+#define CF_SERVER               (1u<<1)  ///< cvar/command that only the server can change/execute
+#define CF_CLIENT_FROM_SERVER   (1u<<2)  ///< command that the server is allowed to execute on the client
+#define CF_SERVER_FROM_CLIENT   (1u<<3)  ///< command the client is allowed to execute on the server as a stringcmd
+#define CF_CHEAT                (1u<<4)  ///< command or cvar that gives an unfair advantage over other players and is blocked unless sv_cheats is 1
+#define CF_ARCHIVE              (1u<<5)  ///< cvar should have its set value saved to config.cfg and persist across sessions
+#define CF_READONLY             (1u<<6)  ///< cvar cannot be changed from the console or the command buffer, and is considered CF_PERSISTENT
+#define CF_NOTIFY               (1u<<7)  ///< cvar should trigger a chat notification to all connected clients when changed
+#define CF_SERVERINFO           (1u<<8)  ///< command or cvar relevant to serverinfo string handling
+#define CF_USERINFO             (1u<<9)  ///< command or cvar used to communicate userinfo to the server
+#define CF_PERSISTENT           (1u<<10) ///< cvar must not be reset on gametype switch (such as scr_screenshot_name, which otherwise isn't set to the mod name properly)
+#define CF_PRIVATE              (1u<<11) ///< cvar should not be $ expanded or sent to the server under any circumstances (rcon_password, etc)
+#define CF_MAXFLAGSVAL          ((1u<<12) - 1) ///< used to determine if flags is valid
 // for internal use only!
-#define CF_DEFAULTSET (1<<30)
-#define CF_ALLOCATED (1<<31)
+#define CF_REGISTERED           (1u<<29) ///< created by Cvar_RegisterVariable()
+#define CF_DEFAULTSET           (1u<<30)
+#define CF_ALLOCATED            (1u<<31) ///< created by Cvar_Get() (console or QC)
+// UBSan: unsigned literals because left shifting by 31 causes signed overflow, although it works as expected on x86.
 
-#define CF_SHARED 3
+#define CF_SHARED (CF_CLIENT | CF_SERVER)
 
 typedef void(*xcommand_t) (struct cmd_state_s *cmd);
 
 typedef enum cmd_source_s
 {
-       src_client,             ///< came in over a net connection as a clc_stringcmd
-                                       ///< host_client will be valid during this state.
-       src_local               ///< from the command buffer
+       src_client,  ///< came in over a net connection as a clc_stringcmd
+                    ///< host_client will be valid during this state.
+       src_local    ///< from the command buffer
 } cmd_source_t;
 
 typedef struct cmd_alias_s
@@ -78,20 +80,21 @@ typedef struct cmd_alias_s
        struct cmd_alias_s *next;
        char name[MAX_ALIAS_NAME];
        char *value;
-       qbool initstate; // indicates this command existed at init
-       char *initialvalue; // backup copy of value at init
+       qbool initstate;           ///< indicates this command existed at init
+       char *initialvalue;        ///< backup copy of value at init
 } cmd_alias_t;
 
 typedef struct cmd_function_s
 {
-       int flags;
+       unsigned flags;
        struct cmd_function_s *next;
        const char *name;
        const char *description;
        xcommand_t function;
        qbool qcfunc;
+       struct cmd_function_s *overridden; ///< the engine cmd overriden by this QC cmd, if applicable
        qbool autofunc;
-       qbool initstate; // indicates this command existed at init
+       qbool initstate;                   ///< indicates this command existed at init
 } cmd_function_t;
 
 /// container for user-defined QC functions and aliases, shared between different command interpreters
@@ -133,44 +136,41 @@ typedef struct cmd_state_s
 
        cmd_buf_t *cbuf;
 
-       cmd_userdefined_t *userdefined; // possible csqc functions and aliases to execute
+       cmd_userdefined_t *userdefined;   ///< possible csqc functions and aliases to execute
 
        cmd_function_t *engine_functions;
 
-       struct cvar_state_s *cvars; // which cvar system is this cmd state able to access? (&cvars_all or &cvars_null)
-       int cvars_flagsmask; // which CVAR_* flags should be visible to this interpreter? (CF_CLIENT | CF_SERVER, or just CF_SERVER)
+       struct cvar_state_s *cvars;       ///< which cvar system is this cmd state able to access? (&cvars_all or &cvars_null)
+       unsigned cvars_flagsmask;         ///< which CVAR_* flags should be visible to this interpreter? (CF_CLIENT | CF_SERVER, or just CF_SERVER)
+       unsigned cmd_flagsmask;           ///< cmd flags that identify this interpreter
 
-       int cmd_flags; // cmd flags that identify this interpreter
-
-       qbool (*Handle)(struct cmd_state_s *, struct cmd_function_s *, const char *, enum cmd_source_s);
-       qbool (*NotFound)(struct cmd_state_s *, struct cmd_function_s *, const char *, enum cmd_source_s);
+       qbool (*Handle)(struct cmd_state_s *, struct cmd_function_s *, const char *, size_t, enum cmd_source_s);
 }
 cmd_state_t;
 
-qbool Cmd_Callback(cmd_state_t *cmd, cmd_function_t *func, const char *text, cmd_source_t src);
-qbool Cmd_CL_Callback(cmd_state_t *cmd, cmd_function_t *func, const char *text, cmd_source_t src);
-qbool Cmd_SV_Callback(cmd_state_t *cmd, cmd_function_t *func, const char *text, cmd_source_t src);
-qbool Cmd_SV_NotFound(cmd_state_t *cmd, cmd_function_t *func, const char *text, cmd_source_t src);
+qbool Cmd_Callback(cmd_state_t *cmd, cmd_function_t *func);
+qbool Cmd_CL_Callback(cmd_state_t *cmd, cmd_function_t *func, const char *text, size_t textlen, cmd_source_t src);
+qbool Cmd_SV_Callback(cmd_state_t *cmd, cmd_function_t *func, const char *text, size_t textlen, cmd_source_t src);
 
 typedef struct cmd_input_s
 {
        llist_t list;
        cmd_state_t *source;
-       double delay;
-       size_t size;
-       size_t length;
+       vec_t delay;
+       size_t size;   ///< excludes \0 terminator
+       size_t length; ///< excludes \0 terminator
        char *text;
        qbool pending;
 } cmd_input_t;
 
-extern cmd_userdefined_t cmd_userdefined_all; // aliases and csqc functions
-extern cmd_userdefined_t cmd_userdefined_null; // intentionally empty
+extern cmd_userdefined_t cmd_userdefined_all;  ///< aliases and csqc functions
+extern cmd_userdefined_t cmd_userdefined_null; ///< intentionally empty
 
-// command interpreter for local commands injected by SVQC, CSQC, MQC, server or client engine code
-// uses cmddefs_all
+/// command interpreter for local commands injected by SVQC, CSQC, MQC, server or client engine code
+/// uses cmddefs_all
 extern cmd_state_t *cmd_local;
-// command interpreter for server commands received over network from clients
-// uses cmddefs_null
+/// command interpreter for server commands received over network from clients
+/// uses cmddefs_null
 extern cmd_state_t *cmd_serverfromclient;
 
 extern qbool host_stuffcmdsrun;
@@ -197,10 +197,12 @@ void Cbuf_InsertText (cmd_state_t *cmd, const char *text);
 void Cbuf_Execute (cmd_buf_t *cbuf);
 /*! Performs deferred commands and runs Cbuf_Execute, called by Host_Frame */
 void Cbuf_Frame (cmd_buf_t *cbuf);
+/// Clears all command buffers
+void Cbuf_Clear(cmd_buf_t *cbuf);
 
 //===========================================================================
 
-/*
+/**
 
 Command execution takes a null terminated string, breaks it into tokens,
 then searches for a command or variable that matches the first token.
@@ -214,15 +216,15 @@ not apropriate.
 void Cmd_Init(void);
 void Cmd_Shutdown(void);
 
-// called by Host_Init, this marks cvars, commands and aliases with their init values
+/// called by Host_Init, this marks cvars, commands and aliases with their init values
 void Cmd_SaveInitState(void);
-// called by FS_GameDir_f, this restores cvars, commands and aliases to init values
+/// called by FS_GameDir_f, this restores cvars, commands and aliases to init values
 void Cmd_RestoreInitState(void);
 
-void Cmd_AddCommand(int flags, const char *cmd_name, xcommand_t function, const char *description);
-// called by the init functions of other parts of the program to
-// register commands and functions to call for them.
-// The cmd_name is referenced later, so it should not be in temp memory
+/// called by the init functions of other parts of the program to
+/// register commands and functions to call for them.
+/// The cmd_name is referenced later, so it should not be in temp memory
+void Cmd_AddCommand(unsigned flags, const char *cmd_name, xcommand_t function, const char *description);
 
 /// used by the cvar code to check for cvar / command name overlap
 qbool Cmd_Exists (cmd_state_t *cmd, const char *cmd_name);
@@ -232,41 +234,42 @@ qbool Cmd_Exists (cmd_state_t *cmd, const char *cmd_name);
 const char *Cmd_CompleteCommand (cmd_state_t *cmd, const char *partial);
 
 int Cmd_CompleteAliasCountPossible (cmd_state_t *cmd, const char *partial);
-
 const char **Cmd_CompleteAliasBuildList (cmd_state_t *cmd, const char *partial);
-
 int Cmd_CompleteCountPossible (cmd_state_t *cmd, const char *partial);
-
 const char **Cmd_CompleteBuildList (cmd_state_t *cmd, const char *partial);
-
 void Cmd_CompleteCommandPrint (cmd_state_t *cmd, const char *partial);
-
 const char *Cmd_CompleteAlias (cmd_state_t *cmd, const char *partial);
-
 void Cmd_CompleteAliasPrint (cmd_state_t *cmd, const char *partial);
 
 // Enhanced console completion by Fett erich@heintz.com
-
 // Added by EvilTypeGuy eviltypeguy@qeradiant.com
 
-int Cmd_Argc (cmd_state_t *cmd);
-const char *Cmd_Argv (cmd_state_t *cmd, int arg);
-const char *Cmd_Args (cmd_state_t *cmd);
-// The functions that execute commands get their parameters with these
-// functions. Cmd_Argv(cmd, ) will return an empty string, not a NULL
-// if arg > argc, so string operations are always safe.
+// The functions that execute commands get their parameters with these functions.
+static inline int Cmd_Argc (cmd_state_t *cmd)
+{
+       return cmd->argc;
+}
+/// Cmd_Argv(cmd, ) will return an empty string (not a NULL) if arg > argc, so string operations are always safe.
+static inline const char *Cmd_Argv(cmd_state_t *cmd, int arg)
+{
+       if (arg >= cmd->argc )
+               return cmd->null_string;
+       return cmd->argv[arg];
+}
+static inline const char *Cmd_Args (cmd_state_t *cmd)
+{
+       return cmd->args;
+}
 
 /// Returns the position (1 to argc-1) in the command's argument list
 /// where the given parameter apears, or 0 if not present
 int Cmd_CheckParm (cmd_state_t *cmd, const char *parm);
 
-//void Cmd_TokenizeString (char *text);
-// Takes a null terminated string.  Does not need to be /n terminated.
-// breaks the string up into arg tokens.
-
 /// Parses a single line of text into arguments and tries to execute it.
 /// The text can come from the command buffer, a remote client, or stdin.
-void Cmd_ExecuteString (cmd_state_t *cmd, const char *text, cmd_source_t src, qbool lockmutex);
+void Cmd_ExecuteString(cmd_state_t *cmd, const char *text, size_t textlen, cmd_source_t src, qbool lockmutex);
+/// Like Cmd_ExecuteString, but with variable expansion.
+void Cmd_PreprocessAndExecuteString(cmd_state_t *cmd, const char *text, size_t textlen, cmd_source_t src, qbool lockmutex);
 
 /// quotes a string so that it can be used as a command argument again;
 /// quoteset is a string that contains one or more of ", \, $ and specifies
index f62356b02ca85eb6e1ae4381fd043bfa422e116e..71bc57f0caa81e8c99611804fbb2a7f5c5786ae1 100644 (file)
@@ -21,19 +21,19 @@ struct texture_s;
 typedef struct trace_s
 {
        // if true, the entire trace was in solid (see hitsupercontentsmask)
-       int allsolid;
+       qbool allsolid;
        // if true, the initial point was in solid (see hitsupercontentsmask)
-       int startsolid;
+       qbool startsolid;
        // this is set to true in world.c if startsolid was set in a trace against world
-       int worldstartsolid;
+       qbool worldstartsolid;
        // this is set to true in world.c if startsolid was set in a trace against a SOLID_BSP entity, in other words this is true if the entity is stuck in a door or wall, but not if stuck in another normal entity
-       int bmodelstartsolid;
+       qbool bmodelstartsolid;
        // if true, the trace passed through empty somewhere
        // (set only by Q1BSP tracing)
-       int inopen;
+       qbool inopen;
        // if true, the trace passed through water/slime/lava somewhere
        // (set only by Q1BSP tracing)
-       int inwater;
+       qbool inwater;
        // fraction of the total distance that was traveled before impact
        // in case of impact this is actually nudged a bit off the surface
        // (1.0 = did not hit anything)
@@ -62,7 +62,7 @@ typedef struct trace_s
        const struct texture_s *hittexture;
        // initially false, set when the start leaf is found
        // (set only by Q1BSP tracing and entity box tracing)
-       int startfound;
+       qbool startfound;
        // if startsolid, contains the minimum penetration depth found in the
        // trace, and the normal needed to push it out of that solid
        double startdepth;
index 1406989ba6e4a9c6fccc840a6f650bbf15cb77c5..0dcde1ea082ec5a55cadd84310df9d0e8d1a95ef 100644 (file)
@@ -118,11 +118,12 @@ void COM_InitGameType (void)
 
 void COM_ChangeGameTypeForGameDirs(void)
 {
-       int i;
+       unsigned i, gamemode_count = sizeof(gamemode_info) / sizeof(gamemode_info[0]);
        int index = -1;
        // this will not not change the gamegroup
+
        // first check if a base game (single gamedir) matches
-       for (i = 0;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
+       for (i = 0; i < gamemode_count; i++)
        {
                if (gamemode_info[i].group == com_startupgamegroup && !(gamemode_info[i].gamedirname2 && gamemode_info[i].gamedirname2[0]))
                {
@@ -130,10 +131,21 @@ void COM_ChangeGameTypeForGameDirs(void)
                        break;
                }
        }
-       // now that we have a base game, see if there is a matching derivative game (two gamedirs)
+       // now that we have a base game, see if there is a derivative game matching the startup one (two gamedirs)
+       // bones_was_here: this prevents a Quake expansion (eg Rogue) getting switched to Quake,
+       // and its gamedirname2 (eg "rogue") being lost from the search path, when adding a miscellaneous gamedir.
+       for (i = 0; i < gamemode_count; i++)
+       {
+               if (gamemode_info[i].group == com_startupgamegroup && (gamemode_info[i].gamedirname2 && gamemode_info[i].gamedirname2[0]) && gamemode_info[i].mode == com_startupgamemode)
+               {
+                       index = i;
+                       break;
+               }
+       }
+       // also see if the first gamedir (from -game parm or gamedir command) matches a derivative game (two/three gamedirs)
        if (fs_numgamedirs)
        {
-               for (i = 0;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
+               for (i = 0; i < gamemode_count; i++)
                {
                        if (gamemode_info[i].group == com_startupgamegroup && (gamemode_info[i].gamedirname2 && gamemode_info[i].gamedirname2[0]) && !strcasecmp(fs_gamedirs[0], gamemode_info[i].gamedirname2))
                        {
@@ -195,7 +207,7 @@ static void COM_SetGameType(int index)
                // if there are spaces in the game's network filter name it would
                // cause parse errors in getservers in dpmaster, so we need to replace
                // them with _ characters
-               strlcpy(gamenetworkfilternamebuffer, gamenetworkfiltername, sizeof(gamenetworkfilternamebuffer));
+               dp_strlcpy(gamenetworkfilternamebuffer, gamenetworkfiltername, sizeof(gamenetworkfilternamebuffer));
                while ((s = strchr(gamenetworkfilternamebuffer, ' ')) != NULL)
                        *s = '_';
                gamenetworkfiltername = gamenetworkfilternamebuffer;
index 8412e489edf370737fefacee261a5e8d3d663be7..9af6e357edac8c92c273b9e30b329f2e4fa13719 100644 (file)
@@ -20,33 +20,34 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
 #include "darkplaces.h"
 
-char *InfoString_GetValue(const char *buffer, const char *key, char *value, size_t valuelength)
+size_t InfoString_GetValue(const char *buffer, const char *key, char *value, size_t valuesize)
 {
-       int pos = 0, j;
+       unsigned pos = 0, j;
        size_t keylength;
+
        if (!key)
                key = "";
        keylength = strlen(key);
-       if (valuelength < 1 || !value)
+       if (valuesize < 1 || !value)
        {
                Con_Printf("InfoString_GetValue: no room in value\n");
-               return NULL;
+               return 0;
        }
-       value[0] = 0;
+       value[0] = '\0';
        if (strchr(key, '\\'))
        {
                Con_Printf("InfoString_GetValue: key name \"%s\" contains \\ which is not possible in an infostring\n", key);
-               return NULL;
+               return 0;
        }
        if (strchr(key, '\"'))
        {
                Con_Printf("InfoString_SetValue: key name \"%s\" contains \" which is not allowed in an infostring\n", key);
-               return NULL;
+               return 0;
        }
        if (!key[0])
        {
                Con_Printf("InfoString_GetValue: can not look up a key with no name\n");
-               return NULL;
+               return 0;
        }
        while (buffer[pos] == '\\')
        {
@@ -54,12 +55,12 @@ char *InfoString_GetValue(const char *buffer, const char *key, char *value, size
                                (buffer[pos+1 + keylength] == 0 ||
                                 buffer[pos+1 + keylength] == '\\'))
                {
-                       pos += 1 + (int)keylength;           // Skip \key
+                       pos += 1 + keylength;           // Skip \key
                        if (buffer[pos] == '\\') pos++; // Skip \ before value.
-                       for (j = 0;buffer[pos+j] && buffer[pos+j] != '\\' && j < (int)valuelength - 1;j++)
+                       for (j = 0;buffer[pos+j] && buffer[pos+j] != '\\' && j < valuesize - 1;j++)
                                value[j] = buffer[pos+j];
-                       value[j] = 0;
-                       return value;
+                       value[j] = '\0';
+                       return j;
                }
                if (buffer[pos] == '\\') pos++; // Skip \ before value.
                for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
@@ -67,7 +68,7 @@ char *InfoString_GetValue(const char *buffer, const char *key, char *value, size
                for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
        }
        // if we reach this point the key was not found
-       return NULL;
+       return 0;
 }
 
 void InfoString_SetValue(char *buffer, size_t bufferlength, const char *key, const char *value)
@@ -122,13 +123,13 @@ void InfoString_SetValue(char *buffer, size_t bufferlength, const char *key, con
        {
                // set the key/value and append the remaining text
                char tempbuffer[MAX_INPUTLINE];
-               strlcpy(tempbuffer, buffer + pos2, sizeof(tempbuffer));
+               dp_strlcpy(tempbuffer, buffer + pos2, sizeof(tempbuffer));
                dpsnprintf(buffer + pos, bufferlength - pos, "\\%s\\%s%s", key, value, tempbuffer);
        }
        else
        {
                // just remove the key from the text
-               strlcpy(buffer + pos, buffer + pos2, bufferlength - pos);
+               dp_strlcpy(buffer + pos, buffer + pos2, bufferlength - pos);
        }
 }
 
index 538c1ba2543cb2db890a6f4d4fb04693fd3d8d86..ae274bb7f3c4f31ef467dd2da35bb3c8cefd836a 100644 (file)
@@ -24,7 +24,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include "qtypes.h"
 #include <stdlib.h>
 
-char *InfoString_GetValue(const char *buffer, const char *key, char *value, size_t valuelength);
+/// Returns the number of bytes written to *value excluding the \0 terminator.
+size_t InfoString_GetValue(const char *buffer, const char *key, char *value, size_t valuesize);
 void InfoString_SetValue(char *buffer, size_t bufferlength, const char *key, const char *value);
 void InfoString_Print(char *buffer);
 
index f2110d17f05fefcafb6a9d5901c86d7e9d23fe3f..aaded00d3fc84d2699eeadb51d6d282e125509f4 100644 (file)
--- a/com_msg.c
+++ b/com_msg.c
@@ -30,6 +30,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 ============================================================================
 */
 
+/* Casting to unsigned when shifting by 24 bits here is necessary to prevent UB
+ * caused by shifting outside the range of int on platforms where int is 32 bits.
+ */
 
 float BuffBigFloat (const unsigned char *buffer)
 {
@@ -39,13 +42,13 @@ float BuffBigFloat (const unsigned char *buffer)
                unsigned int i;
        }
        u;
-       u.i = (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
+       u.i = ((unsigned)buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
        return u.f;
 }
 
 int BuffBigLong (const unsigned char *buffer)
 {
-       return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
+       return ((unsigned)buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
 }
 
 short BuffBigShort (const unsigned char *buffer)
@@ -61,13 +64,13 @@ float BuffLittleFloat (const unsigned char *buffer)
                unsigned int i;
        }
        u;
-       u.i = (buffer[3] << 24) | (buffer[2] << 16) | (buffer[1] << 8) | buffer[0];
+       u.i = ((unsigned)buffer[3] << 24) | (buffer[2] << 16) | (buffer[1] << 8) | buffer[0];
        return u.f;
 }
 
 int BuffLittleLong (const unsigned char *buffer)
 {
-       return (buffer[3] << 24) | (buffer[2] << 16) | (buffer[1] << 8) | buffer[0];
+       return ((unsigned)buffer[3] << 24) | (buffer[2] << 16) | (buffer[1] << 8) | buffer[0];
 }
 
 short BuffLittleShort (const unsigned char *buffer)
@@ -287,7 +290,7 @@ int MSG_ReadLittleLong (sizebuf_t *sb)
                return -1;
        }
        sb->readcount += 4;
-       return sb->data[sb->readcount-4] | (sb->data[sb->readcount-3]<<8) | (sb->data[sb->readcount-2]<<16) | (sb->data[sb->readcount-1]<<24);
+       return sb->data[sb->readcount-4] | (sb->data[sb->readcount-3]<<8) | (sb->data[sb->readcount-2]<<16) | ((unsigned)sb->data[sb->readcount-1]<<24);
 }
 
 int MSG_ReadBigLong (sizebuf_t *sb)
@@ -298,7 +301,7 @@ int MSG_ReadBigLong (sizebuf_t *sb)
                return -1;
        }
        sb->readcount += 4;
-       return (sb->data[sb->readcount-4]<<24) + (sb->data[sb->readcount-3]<<16) + (sb->data[sb->readcount-2]<<8) + sb->data[sb->readcount-1];
+       return ((unsigned)sb->data[sb->readcount-4]<<24) + (sb->data[sb->readcount-3]<<16) + (sb->data[sb->readcount-2]<<8) + sb->data[sb->readcount-1];
 }
 
 float MSG_ReadLittleFloat (sizebuf_t *sb)
@@ -314,7 +317,7 @@ float MSG_ReadLittleFloat (sizebuf_t *sb)
                return -1;
        }
        sb->readcount += 4;
-       dat.l = sb->data[sb->readcount-4] | (sb->data[sb->readcount-3]<<8) | (sb->data[sb->readcount-2]<<16) | (sb->data[sb->readcount-1]<<24);
+       dat.l = sb->data[sb->readcount-4] | (sb->data[sb->readcount-3]<<8) | (sb->data[sb->readcount-2]<<16) | ((unsigned)sb->data[sb->readcount-1]<<24);
        return dat.f;
 }
 
@@ -331,27 +334,40 @@ float MSG_ReadBigFloat (sizebuf_t *sb)
                return -1;
        }
        sb->readcount += 4;
-       dat.l = (sb->data[sb->readcount-4]<<24) | (sb->data[sb->readcount-3]<<16) | (sb->data[sb->readcount-2]<<8) | sb->data[sb->readcount-1];
+       dat.l = ((unsigned)sb->data[sb->readcount-4]<<24) | (sb->data[sb->readcount-3]<<16) | (sb->data[sb->readcount-2]<<8) | sb->data[sb->readcount-1];
        return dat.f;
 }
 
 char *MSG_ReadString (sizebuf_t *sb, char *string, size_t maxstring)
 {
-       int c;
        size_t l = 0;
+
        // read string into sbfer, but only store as many characters as will fit
-       while ((c = MSG_ReadByte(sb)) > 0)
-               if (l < maxstring - 1)
-                       string[l++] = c;
-       string[l] = 0;
+       // if dest buffer is full sb->readcount will still be advanced to end of message string
+       while ((string[l] = MSG_ReadByte_opt(sb)) != '\0')
+               if (l < maxstring)
+                       ++l;
        return string;
 }
+size_t MSG_ReadString_len (sizebuf_t *sb, char *string, size_t maxstring)
+{
+       size_t l = 0;
 
-int MSG_ReadBytes (sizebuf_t *sb, int numbytes, unsigned char *out)
+       // read string into sbfer, but only store as many characters as will fit
+       // if dest buffer is full sb->readcount will still be advanced to end of message string
+       while ((string[l] = MSG_ReadByte_opt(sb)) != '\0')
+               if (l < maxstring)
+                       ++l;
+       return l;
+}
+
+size_t MSG_ReadBytes (sizebuf_t *sb, size_t numbytes, unsigned char *out)
 {
-       int l, c;
-       for (l = 0;l < numbytes && (c = MSG_ReadByte(sb)) != -1;l++)
-               out[l] = c;
+       size_t l = 0;
+
+       // when numbytes have been read sb->readcount won't be advanced any further
+       while (l < numbytes && !sb->badread)
+               out[l++] = MSG_ReadByte_opt(sb);
        return l;
 }
 
index 8db1f27025ab61c545583f0f2cba1821ab41ff6d..f4e459af3e104d8a883a3a1447f86f19f044fa2a 100644 (file)
--- a/common.c
+++ b/common.c
@@ -37,6 +37,7 @@ cvar_t cl_playermodel = {CF_CLIENT | CF_SERVER | CF_USERINFO | CF_ARCHIVE, "play
 cvar_t cl_playerskin = {CF_CLIENT | CF_SERVER | CF_USERINFO | CF_ARCHIVE, "playerskin", "", "current player skin in Nexuiz/Xonotic"};
 
 char com_token[MAX_INPUTLINE];
+unsigned com_token_len;
 
 //===========================================================================
 
@@ -456,16 +457,17 @@ int COM_Wordwrap(const char *string, size_t length, float continuationWidth, flo
 COM_ParseToken_Simple
 
 Parse a token out of a string
+Writes the token and its strlen to the com_token and com_token_len globals.
 ==============
 */
-int COM_ParseToken_Simple(const char **datapointer, qbool returnnewline, qbool parsebackslash, qbool parsecomments)
+qbool COM_ParseToken_Simple(const char **datapointer, qbool returnnewline, qbool parsebackslash, qbool parsecomments)
 {
        int len;
        int c;
        const char *data = *datapointer;
 
-       len = 0;
-       com_token[0] = 0;
+       com_token_len = len = 0;
+       com_token[0] = '\0';
 
        if (!data)
        {
@@ -530,7 +532,8 @@ skipwhite:
                        if (len < (int)sizeof(com_token) - 1)
                                com_token[len++] = c;
                }
-               com_token[len] = 0;
+               com_token[len] = '\0';
+               com_token_len = len;
                if (*data == '\"')
                        data++;
                *datapointer = data;
@@ -540,7 +543,8 @@ skipwhite:
        {
                // translate Mac line ending to UNIX
                com_token[len++] = '\n';data++;
-               com_token[len] = 0;
+               com_token[len] = '\0';
+               com_token_len = len;
                *datapointer = data;
                return true;
        }
@@ -548,7 +552,8 @@ skipwhite:
        {
                // single character
                com_token[len++] = *data++;
-               com_token[len] = 0;
+               com_token[len] = '\0';
+               com_token_len = len;
                *datapointer = data;
                return true;
        }
@@ -558,7 +563,8 @@ skipwhite:
                for (;!ISWHITESPACE(*data);data++)
                        if (len < (int)sizeof(com_token) - 1)
                                com_token[len++] = *data;
-               com_token[len] = 0;
+               com_token[len] = '\0';
+               com_token_len = len;
                *datapointer = data;
                return true;
        }
@@ -569,16 +575,17 @@ skipwhite:
 COM_ParseToken_QuakeC
 
 Parse a token out of a string
+Writes the token and its strlen to the com_token and com_token_len globals.
 ==============
 */
-int COM_ParseToken_QuakeC(const char **datapointer, qbool returnnewline)
+qbool COM_ParseToken_QuakeC(const char **datapointer, qbool returnnewline)
 {
        int len;
        int c;
        const char *data = *datapointer;
 
-       len = 0;
-       com_token[0] = 0;
+       com_token_len = len = 0;
+       com_token[0] = '\0';
 
        if (!data)
        {
@@ -644,7 +651,8 @@ skipwhite:
                        if (len < (int)sizeof(com_token) - 1)
                                com_token[len++] = c;
                }
-               com_token[len] = 0;
+               com_token[len] = '\0';
+               com_token_len = len;
                if (*data == quote)
                        data++;
                *datapointer = data;
@@ -654,7 +662,8 @@ skipwhite:
        {
                // translate Mac line ending to UNIX
                com_token[len++] = '\n';data++;
-               com_token[len] = 0;
+               com_token[len] = '\0';
+               com_token_len = len;
                *datapointer = data;
                return true;
        }
@@ -662,7 +671,8 @@ skipwhite:
        {
                // single character
                com_token[len++] = *data++;
-               com_token[len] = 0;
+               com_token[len] = '\0';
+               com_token_len = len;
                *datapointer = data;
                return true;
        }
@@ -672,7 +682,8 @@ skipwhite:
                for (;!ISWHITESPACE(*data) && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++)
                        if (len < (int)sizeof(com_token) - 1)
                                com_token[len++] = *data;
-               com_token[len] = 0;
+               com_token[len] = '\0';
+               com_token_len = len;
                *datapointer = data;
                return true;
        }
@@ -683,16 +694,17 @@ skipwhite:
 COM_ParseToken_VM_Tokenize
 
 Parse a token out of a string
+Writes the token and its strlen to the com_token and com_token_len globals.
 ==============
 */
-int COM_ParseToken_VM_Tokenize(const char **datapointer, qbool returnnewline)
+qbool COM_ParseToken_VM_Tokenize(const char **datapointer, qbool returnnewline)
 {
        int len;
        int c;
        const char *data = *datapointer;
 
-       len = 0;
-       com_token[0] = 0;
+       com_token_len = len = 0;
+       com_token[0] = '\0';
 
        if (!data)
        {
@@ -758,7 +770,8 @@ skipwhite:
                        if (len < (int)sizeof(com_token) - 1)
                                com_token[len++] = c;
                }
-               com_token[len] = 0;
+               com_token[len] = '\0';
+               com_token_len = len;
                if (*data == quote)
                        data++;
                *datapointer = data;
@@ -768,7 +781,8 @@ skipwhite:
        {
                // translate Mac line ending to UNIX
                com_token[len++] = '\n';data++;
-               com_token[len] = 0;
+               com_token[len] = '\0';
+               com_token_len = len;
                *datapointer = data;
                return true;
        }
@@ -776,7 +790,8 @@ skipwhite:
        {
                // single character
                com_token[len++] = *data++;
-               com_token[len] = 0;
+               com_token[len] = '\0';
+               com_token_len = len;
                *datapointer = data;
                return true;
        }
@@ -786,7 +801,8 @@ skipwhite:
                for (;!ISWHITESPACE(*data) && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++)
                        if (len < (int)sizeof(com_token) - 1)
                                com_token[len++] = *data;
-               com_token[len] = 0;
+               com_token[len] = '\0';
+               com_token_len = len;
                *datapointer = data;
                return true;
        }
@@ -797,15 +813,16 @@ skipwhite:
 COM_ParseToken_Console
 
 Parse a token out of a string, behaving like the qwcl console
+Writes the token and its strlen to the com_token and com_token_len globals.
 ==============
 */
-int COM_ParseToken_Console(const char **datapointer)
+qbool COM_ParseToken_Console(const char **datapointer)
 {
        int len;
        const char *data = *datapointer;
 
-       len = 0;
-       com_token[0] = 0;
+       com_token_len = len = 0;
+       com_token[0] = '\0';
 
        if (!data)
        {
@@ -843,7 +860,8 @@ skipwhite:
                        if (len < (int)sizeof(com_token) - 1)
                                com_token[len++] = *data;
                }
-               com_token[len] = 0;
+               com_token[len] = '\0';
+               com_token_len = len;
                if (*data == '\"')
                        data++;
                *datapointer = data;
@@ -854,7 +872,8 @@ skipwhite:
                for (;!ISWHITESPACE(*data);data++)
                        if (len < (int)sizeof(com_token) - 1)
                                com_token[len++] = *data;
-               com_token[len] = 0;
+               com_token[len] = '\0';
+               com_token_len = len;
                *datapointer = data;
        }
 
@@ -1000,6 +1019,11 @@ int dpvsnprintf (char *buffer, size_t buffersize, const char *format, va_list ar
        if (result < 0 || (size_t)result >= buffersize)
        {
                buffer[buffersize - 1] = '\0';
+               // we could be inside Con_Printf
+               if (result < 0)
+                       Sys_Printf("dpvsnprintf: output error, buffer size %lu\n", (unsigned long)buffersize);
+               else
+                       Sys_Printf("dpvsnprintf: truncated to %lu bytes: \"%s\"\n", (unsigned long)buffersize - 1, buffer);
                return -1;
        }
 
@@ -1009,10 +1033,12 @@ int dpvsnprintf (char *buffer, size_t buffersize, const char *format, va_list ar
 
 //======================================
 
-void COM_ToLowerString (const char *in, char *out, size_t size_out)
+size_t COM_ToLowerString(const char *in, char *out, size_t size_out)
 {
+       const char *out_start = out;
+
        if (size_out == 0)
-               return;
+               return 0;
 
        if(utf8_enable.integer)
        {
@@ -1023,12 +1049,12 @@ void COM_ToLowerString (const char *in, char *out, size_t size_out)
                        Uchar ch = u8_getchar_utf8_enabled(in, &in);
                        ch = u8_tolower(ch);
                        n = u8_fromchar(ch, out, size_out);
+                       out += n; // before the break so the return is correct
                        if(n <= 0)
                                break;
-                       out += n;
                        size_out -= n;
                }
-               return;
+               return out - out_start;
        }
 
        while (*in && size_out > 1)
@@ -1040,12 +1066,15 @@ void COM_ToLowerString (const char *in, char *out, size_t size_out)
                size_out--;
        }
        *out = '\0';
+       return out - out_start;
 }
 
-void COM_ToUpperString (const char *in, char *out, size_t size_out)
+size_t COM_ToUpperString(const char *in, char *out, size_t size_out)
 {
+       const char *out_start = out;
+
        if (size_out == 0)
-               return;
+               return 0;
 
        if(utf8_enable.integer)
        {
@@ -1056,12 +1085,12 @@ void COM_ToUpperString (const char *in, char *out, size_t size_out)
                        Uchar ch = u8_getchar_utf8_enabled(in, &in);
                        ch = u8_toupper(ch);
                        n = u8_fromchar(ch, out, size_out);
+                       out += n; // before the break so the return is correct
                        if(n <= 0)
                                break;
-                       out += n;
                        size_out -= n;
                }
-               return;
+               return out - out_start;
        }
 
        while (*in && size_out > 1)
@@ -1073,6 +1102,7 @@ void COM_ToUpperString (const char *in, char *out, size_t size_out)
                size_out--;
        }
        *out = '\0';
+       return out - out_start;
 }
 
 int COM_StringBeginsWith(const char *s, const char *match)
@@ -1226,9 +1256,10 @@ removes color codes from a string.
 If escape_carets is true, the resulting string will be safe for printing. If
 escape_carets is false, the function will just strip color codes (for logging
 for example).
+Returns the number of bytes written to the *out buffer excluding the \0 terminator.
 
-If the output buffer size did not suffice for converting, the function returns
-FALSE. Generally, if escape_carets is false, the output buffer needs
+If the output buffer size did not suffice for converting, the function returns 0.
+Generally, if escape_carets is false, the output buffer needs
 strlen(str)+1 bytes, and if escape_carets is true, it can need strlen(str)*1.5+2
 bytes. In any case, the function makes sure that the resulting string is
 zero terminated.
@@ -1237,20 +1268,22 @@ For size_in, specify the maximum number of characters from in to use, or 0 to us
 all characters until the zero terminator.
 ============
 */
-qbool
-COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out, qbool escape_carets)
+size_t COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out, qbool escape_carets)
 {
-#define APPEND(ch) do { if(--size_out) { *out++ = (ch); } else { *out++ = 0; return false; } } while(0)
+#define APPEND(ch) do { if(--size_out) { *out++ = (ch); } else { *out++ = 0; return 0; } } while(0)
+
+       const char *out_start = out;
        const char *end = size_in ? (in + size_in) : NULL;
+
        if(size_out < 1)
-               return false;
+               return 0;
        for(;;)
        {
                switch((in == end) ? 0 : *in)
                {
                        case 0:
-                               *out++ = 0;
-                               return true;
+                               *out = '\0';
+                               return out - out_start;
                        case STRING_COLOR_TAG:
                                ++in;
                                switch((in == end) ? 0 : *in)
@@ -1273,8 +1306,8 @@ COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out,
                                                // finish the code by appending another caret when escaping
                                                if(escape_carets)
                                                        APPEND(STRING_COLOR_TAG);
-                                               *out++ = 0;
-                                               return true;
+                                               *out = '\0';
+                                               return out - out_start;
                                        case STRING_COLOR_TAG: // escaped ^
                                                APPEND(STRING_COLOR_TAG);
                                                // append a ^ twice when escaping
@@ -1300,87 +1333,90 @@ COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out,
 #undef APPEND
 }
 
-//========================================================
-// strlcat and strlcpy, from OpenBSD
 
 /*
- * Copyright (c) 1998, 2015 Todd C. Miller <millert@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-/*     $OpenBSD: strlcat.c,v 1.19 2019/01/25 00:19:25 millert Exp $    */
-/*     $OpenBSD: strlcpy.c,v 1.16 2019/01/25 00:19:25 millert Exp $    */
+============
+String Copying
 
+The glibc implementation of memccpy is several times faster than old functions that
+copy one byte at a time (even at -O3) and its advantage increases with string length.
+============
+*/
+#ifdef WIN32
+       // memccpy() is standard in POSIX.1-2001, POSIX.1-2008, SVr4, 4.3BSD, C23.
+       // Microsoft supports it, but apparently complains if we use it.
+       #undef memccpy
+       #define memccpy _memccpy
+#endif
 
-#ifndef HAVE_STRLCAT
-size_t
-strlcat(char *dst, const char *src, size_t dsize)
+/** Chain-copies a string with truncation and efficiency (compared to strlcat()).
+ * The destination ends at an absolute pointer instead of a relative offset
+ * and a pointer to the \0 terminator is returned on success.
+ * Truncates, warns, and returns the end pointer on overflow or unterminated source.
+ * Guarantees \0 termination.    end = dst + sizeof(src[])
+ */
+char *dp_stpecpy(char *dst, char *end, const char *src)
 {
-       const char *odst = dst;
-       const char *osrc = src;
-       size_t n = dsize;
-       size_t dlen;
-
-       /* Find the end of dst and adjust bytes left but don't go past end. */
-       while (n-- != 0 && *dst != '\0')
-               dst++;
-       dlen = dst - odst;
-       n = dsize - dlen;
-
-       if (n-- == 0)
-               return(dlen + strlen(src));
-       while (*src != '\0') {
-               if (n != 0) {
-                       *dst++ = *src;
-                       n--;
-               }
-               src++;
-       }
-       *dst = '\0';
+       char *p = (char *)memccpy(dst, src, '\0', end - dst);
 
-       return(dlen + (src - osrc));    /* count does not include NUL */
+       if (p)
+               return p - 1;
+       end[-1] = '\0';
+       Con_Printf(CON_WARN "%s: src string unterminated or truncated to %lu bytes: \"%s\"\n", __func__, (unsigned long)(dst == end ? 0 : (end - dst) - 1), dst);
+       return end;
 }
-#endif  // #ifndef HAVE_STRLCAT
 
+/** Copies a measured byte sequence (unterminated string) to a null-terminated string.
+ * Returns a pointer to the \0 terminator. Guarantees \0 termination.
+ * Compared to ustr2stp(): truncates and warns on overflow.
+ */
+char *dp_ustr2stp(char *dst, size_t dsize, const char *src, size_t slen)
+{
+       if (slen >= dsize)
+       {
+               slen = dsize - 1;
+               Con_Printf(CON_WARN "%s: src string truncated to %lu bytes: \"%.*s\"\n", __func__, (unsigned long)slen, (int)slen, src);
+       }
+       memcpy(dst, src, slen);
+       dst[slen] = '\0';
+       return &dst[slen];
+}
 
-#ifndef HAVE_STRLCPY
-size_t
-strlcpy(char *dst, const char *src, size_t dsize)
+/** Copies a string, like strlcpy() but with a better return: the number of bytes copied
+ * excluding the \0 terminator. Truncates and warns on overflow or unterminated source,
+ * whereas strlcpy() truncates silently and overreads (possibly segfaulting).
+ * Guarantees \0 termination.
+ * See also: dp_stpecpy() and dp_ustr2stp().
+ */
+size_t dp__strlcpy(char *dst, const char *src, size_t dsize, const char *func, unsigned line)
 {
-       const char *osrc = src;
-       size_t nleft = dsize;
+       char *p = (char *)memccpy(dst, src, '\0', dsize);
 
-       /* Copy as many bytes as will fit. */
-       if (nleft != 0) {
-               while (--nleft != 0) {
-                       if ((*dst++ = *src++) == '\0')
-                               break;
-               }
-       }
+       if (p)
+               return (p - 1) - dst;
+       dst[dsize - 1] = '\0';
+       Con_Printf(CON_WARN "%s:%u: src string unterminated or truncated to %lu bytes: \"%s\"\n", func, line, (unsigned long)dsize - 1, dst);
+       return dsize - 1;
+}
 
-       /* Not enough room in dst, add NUL and traverse rest of src. */
-       if (nleft == 0) {
-               if (dsize != 0)
-                       *dst = '\0';            /* NUL-terminate dst */
-               while (*src++)
-                       ;
-       }
+/** Catenates a string, like strlcat() but with a better return: the number of bytes copied
+ * excluding the \0 terminator. Truncates and warns on overflow or unterminated source,
+ * whereas strlcat() truncates silently and overreads (possibly segfaulting).
+ * Guarantees \0 termination.
+ * Inefficient like any strcat(), please use memcpy(), dp_stpecpy() or dp_strlcpy() instead.
+ */
+size_t dp__strlcat(char *dst, const char *src, size_t dsize, const char *func, unsigned line)
+{
+       size_t offset;
+       char *p = (char *)memchr(dst, '\0', dsize);
 
-       return(src - osrc - 1); /* count does not include NUL */
+       if (!p)
+               p = dst;
+       offset = p - dst;
+       return dp__strlcpy(p, src, dsize - offset, func, line) + offset;
 }
 
-#endif  // #ifndef HAVE_STRLCPY
+
 
 void FindFraction(double val, int *num, int *denom, int denomMax)
 {
@@ -1418,7 +1454,7 @@ char **XPM_DecodeString(const char *in)
        while(COM_ParseToken_QuakeC(&in, false))
        {
                tokens[line] = lines[line];
-               strlcpy(lines[line++], com_token, sizeof(lines[0]));
+               dp_strlcpy(lines[line++], com_token, sizeof(lines[0]));
                if(!COM_ParseToken_QuakeC(&in, false))
                        return NULL;
                if(!strcmp(com_token, "}"))
index 0fdf97ce59a3e0f982a7c247198c7a7671707c20..5b73aa6f6c4d9a5e0fd0bff5a21eac16c87ad24a 100644 (file)
--- a/common.h
+++ b/common.h
@@ -180,10 +180,14 @@ int MSG_ReadBigLong (sizebuf_t *sb);
 float MSG_ReadLittleFloat (sizebuf_t *sb);
 float MSG_ReadBigFloat (sizebuf_t *sb);
 char *MSG_ReadString (sizebuf_t *sb, char *string, size_t maxstring);
-int MSG_ReadBytes (sizebuf_t *sb, int numbytes, unsigned char *out);
+/// Same as MSG_ReadString except it returns the number of bytes written to *string excluding the \0 terminator.
+size_t MSG_ReadString_len (sizebuf_t *sb, char *string, size_t maxstring);
+size_t MSG_ReadBytes (sizebuf_t *sb, size_t numbytes, unsigned char *out);
 
 #define MSG_ReadChar(sb) ((sb)->readcount >= (sb)->cursize ? ((sb)->badread = true, -1) : (signed char)(sb)->data[(sb)->readcount++])
 #define MSG_ReadByte(sb) ((sb)->readcount >= (sb)->cursize ? ((sb)->badread = true, -1) : (unsigned char)(sb)->data[(sb)->readcount++])
+/// Same as MSG_ReadByte but with no need to copy twice (first to `int` to check for -1) so each byte can be copied directly to a string[]
+#define MSG_ReadByte_opt(sb) ((sb)->readcount >= (sb)->cursize ? ((sb)->badread = true, '\0') : (unsigned char)(sb)->data[(sb)->readcount++])
 #define MSG_ReadShort MSG_ReadLittleShort
 #define MSG_ReadLong MSG_ReadLittleLong
 #define MSG_ReadFloat MSG_ReadLittleFloat
@@ -205,11 +209,11 @@ typedef int (*COM_LineProcessorFunc) (void *passthrough, const char *line, size_
 int COM_Wordwrap(const char *string, size_t length, float continuationSize, float maxWidth, COM_WordWidthFunc_t wordWidth, void *passthroughCW, COM_LineProcessorFunc processLine, void *passthroughPL);
 
 extern char com_token[MAX_INPUTLINE];
-
-int COM_ParseToken_Simple(const char **datapointer, qbool returnnewline, qbool parsebackslash, qbool parsecomments);
-int COM_ParseToken_QuakeC(const char **datapointer, qbool returnnewline);
-int COM_ParseToken_VM_Tokenize(const char **datapointer, qbool returnnewline);
-int COM_ParseToken_Console(const char **datapointer);
+extern unsigned com_token_len;
+qbool COM_ParseToken_Simple(const char **datapointer, qbool returnnewline, qbool parsebackslash, qbool parsecomments);
+qbool COM_ParseToken_QuakeC(const char **datapointer, qbool returnnewline);
+qbool COM_ParseToken_VM_Tokenize(const char **datapointer, qbool returnnewline);
+qbool COM_ParseToken_Console(const char **datapointer);
 
 void COM_Init (void);
 void COM_Shutdown (void);
@@ -225,35 +229,45 @@ char *va(char *buf, size_t buflen, const char *format, ...) DP_FUNC_PRINTF(3);
 #endif
 
 // snprintf and vsnprintf are NOT portable. Use their DP counterparts instead
-#ifdef snprintf
-# undef snprintf
-#endif
+#undef snprintf
 #define snprintf DP_STATIC_ASSERT(0, "snprintf is forbidden for portability reasons. Use dpsnprintf instead.")
-#ifdef vsnprintf
-# undef vsnprintf
-#endif
+#undef vsnprintf
 #define vsnprintf DP_STATIC_ASSERT(0, "vsnprintf is forbidden for portability reasons. Use dpvsnprintf instead.")
 
-// dpsnprintf and dpvsnprintf
-// return the number of printed characters, excluding the final '\0'
-// or return -1 if the buffer isn't big enough to contain the entire string.
-// buffer is ALWAYS null-terminated
+// documentation duplicated deliberately for the benefit of IDEs that support https://www.doxygen.nl/manual/docblocks.html
+/// Returns the number of printed characters, excluding the final '\0'
+/// or returns -1 if the buffer isn't big enough to contain the entire string.
+/// Buffer is ALWAYS null-terminated.
 extern int dpsnprintf (char *buffer, size_t buffersize, const char *format, ...) DP_FUNC_PRINTF(3);
+/// Returns the number of printed characters, excluding the final '\0'
+/// or returns -1 if the buffer isn't big enough to contain the entire string.
+/// Buffer is ALWAYS null-terminated.
 extern int dpvsnprintf (char *buffer, size_t buffersize, const char *format, va_list args);
 
 // A bunch of functions are forbidden for security reasons (and also to please MSVS 2005, for some of them)
 // LadyHavoc: added #undef lines here to avoid warnings in Linux
 #undef strcat
-#define strcat DP_STATIC_ASSERT(0, "strcat is forbidden for security reasons. Use strlcat or memcpy instead.")
+#define strcat DP_STATIC_ASSERT(0, "strcat is forbidden for security reasons. Use dp_strlcat or memcpy instead.")
 #undef strncat
-#define strncat DP_STATIC_ASSERT(0, "strncat is forbidden for security reasons. Use strlcat or memcpy instead.")
+#define strncat DP_STATIC_ASSERT(0, "strncat is forbidden for security reasons. Use dp_strlcat or memcpy instead.")
 #undef strcpy
-#define strcpy DP_STATIC_ASSERT(0, "strcpy is forbidden for security reasons. Use strlcpy or memcpy instead.")
+#define strcpy DP_STATIC_ASSERT(0, "strcpy is forbidden for security reasons. Use dp_strlcpy or memcpy instead.")
 #undef strncpy
-#define strncpy DP_STATIC_ASSERT(0, "strncpy is forbidden for security reasons. Use strlcpy or memcpy instead.")
+#define strncpy DP_STATIC_ASSERT(0, "strncpy is forbidden for security reasons. Use dp_strlcpy or memcpy instead.")
+#undef stpcpy
+#define stpcpy DP_STATIC_ASSERT(0, "stpcpy is forbidden for security reasons. Use dp_stpecpy or memcpy instead.")
+#undef ustpcpy
+#define ustpcpy DP_STATIC_ASSERT(0, "ustpcpy is forbidden for security reasons. Use dp_ustr2stp or memcpy instead.")
+#undef ustr2stp
+#define ustr2stp DP_STATIC_ASSERT(0, "ustr2stp is forbidden for security reasons. Use dp_ustr2stp or memcpy instead.")
 #undef sprintf
 #define sprintf DP_STATIC_ASSERT(0, "sprintf is forbidden for security reasons. Use dpsnprintf instead.")
 
+#undef strlcpy
+#define strlcpy DP_STATIC_ASSERT(0, "strlcpy is forbidden for stability and correctness. See common.h and common.c comments.")
+#undef strlcat
+#define strlcat DP_STATIC_ASSERT(0, "strlcat is forbidden for stability and correctness. See common.h and common.c comments.")
+
 
 //============================================================================
 
@@ -270,44 +284,24 @@ typedef enum userdirmode_e
 }
 userdirmode_t;
 
-void COM_ToLowerString (const char *in, char *out, size_t size_out);
-void COM_ToUpperString (const char *in, char *out, size_t size_out);
+/// Returns the number of bytes written to *out excluding the \0 terminator.
+size_t COM_ToLowerString(const char *in, char *out, size_t size_out);
+/// Returns the number of bytes written to *out excluding the \0 terminator.
+size_t COM_ToUpperString(const char *in, char *out, size_t size_out);
 int COM_StringBeginsWith(const char *s, const char *match);
-
 int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *tokenbuf, int tokenbufsize, const char *commentprefix);
-
 size_t COM_StringLengthNoColors(const char *s, size_t size_s, qbool *valid);
-qbool COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out, qbool escape_carets);
-void COM_ToLowerString (const char *in, char *out, size_t size_out);
-void COM_ToUpperString (const char *in, char *out, size_t size_out);
-
-// strlcat and strlcpy, from OpenBSD
-// Most (all?) BSDs already have them
-#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(MACOSX)
-# define HAVE_STRLCAT 1
-# define HAVE_STRLCPY 1
-#endif
+size_t COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out, qbool escape_carets);
 
-#ifndef HAVE_STRLCAT
-/*!
- * Appends src to string dst of size dsize (unlike strncat, dsize is the
- * full size of dst, not space left).  At most dsize-1 characters
- * will be copied.  Always NUL terminates (unless dsize <= strlen(dst)).
- * Returns strlen(src) + MIN(dsize, strlen(initial dst)).
- * If retval >= dsize, truncation occurred.
- */
-size_t strlcat(char *dst, const char *src, size_t dsize);
-#endif  // #ifndef HAVE_STRLCAT
-
-#ifndef HAVE_STRLCPY
-/*!
- * Copy string src to buffer dst of size dsize.  At most dsize-1
- * chars will be copied.  Always NUL terminates (unless dsize == 0).
- * Returns strlen(src); if retval >= dsize, truncation occurred.
- */
-size_t strlcpy(char *dst, const char *src, size_t dsize);
 
-#endif  // #ifndef HAVE_STRLCPY
+
+#define dp_strlcpy(dst, src, dsize) dp__strlcpy(dst, src, dsize, __func__, __LINE__)
+#define dp_strlcat(dst, src, dsize) dp__strlcat(dst, src, dsize, __func__, __LINE__)
+size_t dp__strlcpy(char *dst, const char *src, size_t dsize, const char *func, unsigned line);
+size_t dp__strlcat(char *dst, const char *src, size_t dsize, const char *func, unsigned line);
+char *dp_stpecpy(char *dst, char *end, const char *src);
+char *dp_ustr2stp(char *dst, size_t dsize, const char *src, size_t slen);
+
 
 void FindFraction(double val, int *num, int *denom, int denomMax);
 
index cd596418592a0b00fef50a234173c7b65a1bc8b5..21782f7f439e1edbb6db6a99fd9bf821cd492db0 100644 (file)
--- a/console.c
+++ b/console.c
@@ -333,7 +333,7 @@ ConBuffer_AddLine
 Appends a given string as a new line to the console.
 ================
 */
-void ConBuffer_AddLine(conbuffer_t *buf, const char *line, int len, int mask)
+void ConBuffer_AddLine(conbuffer_t *buf, const char *line, int len, unsigned mask)
 {
        char *putpos;
        con_lineinfo_t *p;
@@ -367,7 +367,7 @@ void ConBuffer_AddLine(conbuffer_t *buf, const char *line, int len, int mask)
        p->height = -1; // calculate when needed
 }
 
-int ConBuffer_FindPrevLine(conbuffer_t *buf, int mask_must, int mask_mustnot, int start)
+int ConBuffer_FindPrevLine(conbuffer_t *buf, unsigned mask_must, unsigned mask_mustnot, int start)
 {
        int i;
        if(start == -1)
@@ -391,8 +391,8 @@ const char *ConBuffer_GetLine(conbuffer_t *buf, int i)
 {
        static char copybuf[MAX_INPUTLINE]; // client only
        con_lineinfo_t *l = &CONBUFFER_LINES(buf, i);
-       size_t sz = l->len+1 > sizeof(copybuf) ? sizeof(copybuf) : l->len+1;
-       strlcpy(copybuf, l->start, sz);
+
+       dp_ustr2stp(copybuf, sizeof(copybuf), l->start, l->len);
        return copybuf;
 }
 
@@ -512,7 +512,7 @@ static void Log_Open (void)
        logfile = FS_OpenRealFile(log_file.string, "a", false);
        if (logfile != NULL)
        {
-               strlcpy (crt_log_file, log_file.string, sizeof (crt_log_file));
+               dp_strlcpy (crt_log_file, log_file.string, sizeof (crt_log_file));
                FS_Print (logfile, Log_Timestamp ("Log started"));
        }
 }
@@ -711,38 +711,42 @@ void Con_ClearNotify (void)
                        CON_LINES(i).mask |= CON_MASK_HIDENOTIFY;
 }
 
+static void Con_MsgCmdMode(cmd_state_t *cmd, signed char mode)
+{
+       if (cls.demoplayback && mode >= 0)
+               return;
+       key_dest = key_message;
+       chat_mode = mode;
+       if(Cmd_Argc(cmd) > 1)
+       {
+               chat_bufferpos = dpsnprintf(chat_buffer, sizeof(chat_buffer), "%s ", Cmd_Args(cmd));
+               if (chat_bufferpos < 0)
+                       chat_bufferpos = 0;
+       }
+}
 
 /*
 ================
 Con_MessageMode_f
+
+"say"
 ================
 */
 static void Con_MessageMode_f(cmd_state_t *cmd)
 {
-       key_dest = key_message;
-       chat_mode = 0; // "say"
-       if(Cmd_Argc(cmd) > 1)
-       {
-               dpsnprintf(chat_buffer, sizeof(chat_buffer), "%s ", Cmd_Args(cmd));
-               chat_bufferpos = (unsigned int)strlen(chat_buffer);
-       }
+       Con_MsgCmdMode(cmd, 0);
 }
 
-
 /*
 ================
 Con_MessageMode2_f
+
+"say_team"
 ================
 */
 static void Con_MessageMode2_f(cmd_state_t *cmd)
 {
-       key_dest = key_message;
-       chat_mode = 1; // "say_team"
-       if(Cmd_Argc(cmd) > 1)
-       {
-               dpsnprintf(chat_buffer, sizeof(chat_buffer), "%s ", Cmd_Args(cmd));
-               chat_bufferpos = (unsigned int)strlen(chat_buffer);
-       }
+       Con_MsgCmdMode(cmd, 1);
 }
 
 /*
@@ -752,13 +756,7 @@ Con_CommandMode_f
 */
 static void Con_CommandMode_f(cmd_state_t *cmd)
 {
-       key_dest = key_message;
-       if(Cmd_Argc(cmd) > 1)
-       {
-               dpsnprintf(chat_buffer, sizeof(chat_buffer), "%s ", Cmd_Args(cmd));
-               chat_bufferpos = (unsigned int)strlen(chat_buffer);
-       }
-       chat_mode = -1; // command
+       Con_MsgCmdMode(cmd, -1);
 }
 
 /*
@@ -933,8 +931,6 @@ void Con_Init (void)
        Cmd_AddCommand(CF_SHARED, "condump", Con_ConDump_f, "output console history to a file (see also log_file)");
 
        con_initialized = true;
-
-       Con_Print("Console initialized.\n");
 }
 
 void Con_Shutdown (void)
@@ -942,7 +938,8 @@ void Con_Shutdown (void)
        if (con_mutex) Thread_LockMutex(con_mutex);
        ConBuffer_Shutdown(&con);
        if (con_mutex) Thread_UnlockMutex(con_mutex);
-       if (con_mutex) Thread_DestroyMutex(con_mutex);con_mutex = NULL;
+       if (con_mutex) Thread_DestroyMutex(con_mutex);
+       con_mutex = NULL;
 }
 
 /*
@@ -1154,11 +1151,10 @@ Con_MaskPrint
 */
 extern cvar_t timestamps;
 extern cvar_t timeformat;
-extern qbool sys_nostdout;
-void Con_MaskPrint(int additionalmask, const char *msg)
+void Con_MaskPrint(unsigned additionalmask, const char *msg)
 {
-       static int mask = 0;
-       static int index = 0;
+       static unsigned mask = 0;
+       static unsigned index = 0;
        static char line[MAX_INPUTLINE];
 
        if (con_mutex)
@@ -1223,7 +1219,7 @@ void Con_MaskPrint(int additionalmask, const char *msg)
                                Con_PrintToHistory(line, mask);
                        }
                        // send to terminal or dedicated server window
-                       if (!sys_nostdout)
+                       if (sys.outfd >= 0)
                        if (developer.integer || !(mask & CON_MASK_DEVELOPER))
                        {
                                if(sys_specialcharactertranslation.integer)
@@ -1360,12 +1356,12 @@ void Con_MaskPrint(int additionalmask, const char *msg)
                                                *out++ = '[';
                                                *out++ = 'm';
                                        }
-                                       *out++ = 0;
-                                       Sys_Print(printline);
+                                       *out = '\0';
+                                       Sys_Print(printline, out - printline);
                                }
                                else if(sys_colortranslation.integer == 2) // Quake
                                {
-                                       Sys_Print(line);
+                                       Sys_Print(line, index);
                                }
                                else // strip
                                {
@@ -1415,8 +1411,8 @@ void Con_MaskPrint(int additionalmask, const char *msg)
                                                                break;
                                                }
                                        }
-                                       *out++ = 0;
-                                       Sys_Print(printline);
+                                       *out = '\0';
+                                       Sys_Print(printline, out - printline);
                                }
                        }
                        // empty the line buffer
@@ -1434,7 +1430,7 @@ void Con_MaskPrint(int additionalmask, const char *msg)
 Con_MaskPrintf
 ================
 */
-void Con_MaskPrintf(int mask, const char *fmt, ...)
+void Con_MaskPrintf(unsigned mask, const char *fmt, ...)
 {
        va_list argptr;
        char msg[MAX_INPUTLINE];
@@ -1541,7 +1537,7 @@ static void Con_DrawInput(qbool is_console, float x, float v, float inputsize)
        {
                // empty prefix because ] is part of the console edit line
                prefix = "";
-               strlcpy(text, key_line, sizeof(text));
+               dp_strlcpy(text, key_line, sizeof(text));
                linepos = key_linepos;
                fnt = FONT_CONSOLE;
        }
@@ -1553,7 +1549,7 @@ static void Con_DrawInput(qbool is_console, float x, float v, float inputsize)
                        prefix = "say_team:";
                else
                        prefix = "say:";
-               strlcpy(text, chat_buffer, sizeof(text));
+               dp_strlcpy(text, chat_buffer, sizeof(text));
                linepos = chat_bufferpos;
                fnt = FONT_CHAT;
        }
@@ -1704,7 +1700,7 @@ static int Con_DisplayLineFunc(void *passthrough, const char *line, size_t lengt
        return 1;
 }
 
-static int Con_DrawNotifyRect(int mask_must, int mask_mustnot, float maxage, float x, float y, float width, float height, float fontsize, float alignment_x, float alignment_y, const char *continuationString)
+static int Con_DrawNotifyRect(unsigned mask_must, unsigned mask_mustnot, float maxage, float x, float y, float width, float height, float fontsize, float alignment_x, float alignment_y, const char *continuationString)
 {
        int i;
        int lines = 0;
@@ -1905,7 +1901,7 @@ If alpha is 0, the line is not drawn, but still wrapped and its height
 returned.
 ================
 */
-static int Con_DrawConsoleLine(int mask_must, int mask_mustnot, float y, int lineno, float ymin, float ymax)
+static int Con_DrawConsoleLine(unsigned mask_must, unsigned mask_mustnot, float y, int lineno, float ymin, float ymax)
 {
        float width = vid_conwidth.value;
        con_text_info_t ti;
@@ -1937,7 +1933,7 @@ Calculates the last visible line index and how much to show of it based on
 con_backscroll.
 ================
 */
-static void Con_LastVisibleLine(int mask_must, int mask_mustnot, int *last, int *limitlast)
+static void Con_LastVisibleLine(unsigned mask_must, unsigned mask_mustnot, int *last, int *limitlast)
 {
        int lines_seen = 0;
        int i;
@@ -2154,18 +2150,18 @@ qbool GetMapList (const char *s, char *completedname, int completednamebufferlen
                char entfilename[MAX_QPATH];
                char desc[64];
                desc[0] = 0;
-               strlcpy(message, "^1ERROR: open failed^7", sizeof(message));
+               dp_strlcpy(message, "^1ERROR: open failed^7", sizeof(message));
                p = 0;
                f = FS_OpenVirtualFile(t->filenames[i], true);
                if(f)
                {
-                       strlcpy(message, "^1ERROR: not a known map format^7", sizeof(message));
+                       dp_strlcpy(message, "^1ERROR: not a known map format^7", sizeof(message));
                        memset(buf, 0, 1024);
                        FS_Read(f, buf, 1024);
                        if (!memcmp(buf, "IBSP", 4))
                        {
                                p = LittleLong(((int *)buf)[1]);
-                               if (p == Q3BSPVERSION)
+                               if (p == Q3BSPVERSION || p == Q3BSPVERSION_LIVE || p == Q3BSPVERSION_IG)
                                {
                                        q3dheader_t *header = (q3dheader_t *)buf;
                                        lumpofs = LittleLong(header->lumps[Q3LUMP_ENTITIES].fileofs);
@@ -2215,7 +2211,7 @@ qbool GetMapList (const char *s, char *completedname, int completednamebufferlen
                        }
                        else
                                dpsnprintf(desc, sizeof(desc), "unknown%i", BuffLittleLong(buf));
-                       strlcpy(entfilename, t->filenames[i], sizeof(entfilename));
+                       dp_strlcpy(entfilename, t->filenames[i], sizeof(entfilename));
                        memcpy(entfilename + strlen(entfilename) - 4, ".ent", 5);
                        entities = (char *)FS_LoadFile(entfilename, tempmempool, true, NULL);
                        if (!entities && lumplen >= 10)
@@ -2251,7 +2247,7 @@ qbool GetMapList (const char *s, char *completedname, int completednamebufferlen
                                        if (!strcmp(keyname, "message"))
                                        {
                                                // get the message contents
-                                               strlcpy(message, com_token, sizeof(message));
+                                               dp_strlcpy(message, com_token, sizeof(message));
                                                break;
                                        }
                                }
@@ -2460,10 +2456,10 @@ static int Nicks_CompleteCountPossible(char *line, int pos, char *s, qbool isCon
                if(match < 0)
                        continue;
                //Con_Printf("Possible match: %s|%s\n", cl.scores[p].name, name);
-               strlcpy(Nicks_list[count], cl.scores[p].name, sizeof(Nicks_list[count]));
+               dp_strlcpy(Nicks_list[count], cl.scores[p].name, sizeof(Nicks_list[count]));
 
                // the sanitized list
-               strlcpy(Nicks_sanlist[count], name, sizeof(Nicks_sanlist[count]));
+               dp_strlcpy(Nicks_sanlist[count], name, sizeof(Nicks_sanlist[count]));
                if(!count)
                {
                        Nicks_matchpos = match;
@@ -2577,7 +2573,7 @@ static void Nicks_CutMatchesAlphaNumeric(int count)
        if(Nicks_strcleanlen(Nicks_sanlist[0]) < strlen(tempstr))
        {
                // if the clean sanitized one is longer than the current one, use it, it has crap chars which definitely are in there
-               strlcpy(Nicks_sanlist[0], tempstr, sizeof(Nicks_sanlist[0]));
+               dp_strlcpy(Nicks_sanlist[0], tempstr, sizeof(Nicks_sanlist[0]));
        }
 }
 
@@ -2633,7 +2629,7 @@ static void Nicks_CutMatchesNoSpaces(int count)
        if(Nicks_strcleanlen(Nicks_sanlist[0]) < strlen(tempstr))
        {
                // if the clean sanitized one is longer than the current one, use it, it has crap chars which definitely are in there
-               strlcpy(Nicks_sanlist[0], tempstr, sizeof(Nicks_sanlist[0]));
+               dp_strlcpy(Nicks_sanlist[0], tempstr, sizeof(Nicks_sanlist[0]));
        }
 }
 
@@ -2780,7 +2776,7 @@ int Con_CompleteCommandLine(cmd_state_t *cmd, qbool is_console)
        pos++;
 
        s = line + pos;
-       strlcpy(s2, line + linepos, sizeof(s2)); //save chars after cursor
+       dp_strlcpy(s2, line + linepos, sizeof(s2)); //save chars after cursor
        line[linepos] = 0; //hide them
 
        c = v = a = n = cmd_len = 0;
@@ -2790,7 +2786,8 @@ int Con_CompleteCommandLine(cmd_state_t *cmd, qbool is_console)
        space = strchr(line + 1, ' ');
        if(space && pos == (space - line) + 1)
        {
-               strlcpy(command, line + 1, min(sizeof(command), (unsigned int)(space - line)));
+               // adding 1 to line drops the leading ]
+               dp_ustr2stp(command, sizeof(command), line + 1, space - (line + 1));
 
                patterns = Cvar_VariableString(cmd->cvars, va(vabuf, sizeof(vabuf), "con_completion_%s", command), CF_CLIENT | CF_SERVER); // TODO maybe use a better place for this?
                if(patterns && !*patterns)
@@ -2807,8 +2804,8 @@ int Con_CompleteCommandLine(cmd_state_t *cmd, qbool is_console)
 
                                // and now do the actual work
                                *s = 0;
-                               strlcat(line, t, MAX_INPUTLINE);
-                               strlcat(line, s2, MAX_INPUTLINE); //add back chars after cursor
+                               dp_strlcat(line, t, MAX_INPUTLINE);
+                               dp_strlcat(line, s2, MAX_INPUTLINE); //add back chars after cursor
 
                                // and fix the cursor
                                if(linepos > (int) strlen(line))
@@ -2856,8 +2853,8 @@ int Con_CompleteCommandLine(cmd_state_t *cmd, qbool is_console)
                                                const char *slash = strrchr(s, '/');
                                                if(slash)
                                                {
-                                                       strlcpy(t, s, min(sizeof(t), (unsigned int)(slash - s + 2))); // + 2, because I want to include the slash
-                                                       strlcat(t, com_token, sizeof(t));
+                                                       dp_strlcpy(t, s, min(sizeof(t), (unsigned int)(slash - s + 2))); // + 2, because I want to include the slash
+                                                       dp_strlcat(t, com_token, sizeof(t));
                                                        search = FS_Search(t, true, true, NULL);
                                                }
                                                else
@@ -2879,8 +2876,8 @@ int Con_CompleteCommandLine(cmd_state_t *cmd, qbool is_console)
                                        const char *slash = strrchr(s, '/');
                                        if(slash)
                                        {
-                                               strlcpy(t, s, min(sizeof(t), (unsigned int)(slash - s + 2))); // + 2, because I want to include the slash
-                                               strlcat(t, "*", sizeof(t));
+                                               dp_strlcpy(t, s, min(sizeof(t), (unsigned int)(slash - s + 2))); // + 2, because I want to include the slash
+                                               dp_strlcat(t, "*", sizeof(t));
                                                search = FS_Search(t, true, true, NULL);
                                        }
                                        else
@@ -2939,7 +2936,7 @@ int Con_CompleteCommandLine(cmd_state_t *cmd, qbool is_console)
                                                // of resultbuf.strings[0]. We want to append the characters
                                                // from resultbuf.strings[0] to (not including) p as these are
                                                // the unique prefix
-                                               strlcpy(t, (resultbuf.numstrings > 0 ? resultbuf : dirbuf).strings[0], min(matchchars + 1, sizeof(t)));
+                                               dp_strlcpy(t, (resultbuf.numstrings > 0 ? resultbuf : dirbuf).strings[0], min(matchchars + 1, sizeof(t)));
                                        }
 
                                        // first move the cursor
@@ -2947,8 +2944,8 @@ int Con_CompleteCommandLine(cmd_state_t *cmd, qbool is_console)
 
                                        // and now do the actual work
                                        *s = 0;
-                                       strlcat(line, t, MAX_INPUTLINE);
-                                       strlcat(line, s2, MAX_INPUTLINE); //add back chars after cursor
+                                       dp_strlcat(line, t, MAX_INPUTLINE);
+                                       dp_strlcat(line, s2, MAX_INPUTLINE); //add back chars after cursor
 
                                        // and fix the cursor
                                        if(linepos > (int) strlen(line))
@@ -2993,7 +2990,7 @@ nicks:
        if (!(c + v + a + n))   // No possible matches
        {
                if(s2[0])
-                       strlcpy(&line[linepos], s2, linesize - linepos);
+                       dp_strlcpy(&line[linepos], s2, linesize - linepos);
                return linepos;
        }
 
@@ -3056,7 +3053,7 @@ done:
 
        // use strlcat to avoid a buffer overrun
        line[linepos] = 0;
-       strlcat(line, s2, linesize);
+       dp_strlcat(line, s2, linesize);
 
        if (!is_console)
                return linepos;
index 8ce079351f2b15d354fde1fb035001b8714e9285..10da88f0866f236122abef253d6c4b36a499ed12 100644 (file)
--- a/console.h
+++ b/console.h
@@ -46,10 +46,10 @@ void Con_Shutdown (void);
 void Con_DrawConsole (int lines);
 
 /// Prints to a chosen console target
-void Con_MaskPrint(int mask, const char *msg);
+void Con_MaskPrint(unsigned additionalmask, const char *msg);
 
 // Prints to a chosen console target
-void Con_MaskPrintf(int mask, const char *fmt, ...) DP_FUNC_PRINTF(2);
+void Con_MaskPrintf(unsigned mask, const char *fmt, ...) DP_FUNC_PRINTF(2);
 
 /// Prints to all appropriate console targets, and adds timestamps
 void Con_Print(const char *txt);
@@ -108,7 +108,7 @@ typedef struct con_lineinfo_s
 {
        char *start;
        size_t len;
-       int mask;
+       unsigned mask;
 
        /// used only by console.c
        double addtime;
@@ -149,9 +149,9 @@ void ConBuffer_DeleteLine(conbuffer_t *buf);
 void ConBuffer_DeleteLastLine(conbuffer_t *buf);
 
 /// Appends a given string as a new line to the console.
-void ConBuffer_AddLine(conbuffer_t *buf, const char *line, int len, int mask);
-int ConBuffer_FindPrevLine(conbuffer_t *buf, int mask_must, int mask_mustnot, int start);
-int ConBuffer_FindNextLine(conbuffer_t *buf, int mask_must, int mask_mustnot, int start);
+void ConBuffer_AddLine(conbuffer_t *buf, const char *line, int len, unsigned mask);
+int ConBuffer_FindPrevLine(conbuffer_t *buf, unsigned mask_must, unsigned mask_mustnot, int start);
+int ConBuffer_FindNextLine(conbuffer_t *buf, unsigned mask_must, unsigned mask_mustnot, int start);
 const char *ConBuffer_GetLine(conbuffer_t *buf, int i);
 
 #endif
diff --git a/convex.c b/convex.c
new file mode 100644 (file)
index 0000000..139fd7e
--- /dev/null
+++ b/convex.c
@@ -0,0 +1,238 @@
+/*
+Copyright (c) 2022 Ashley Rose Hale (LadyHavoc)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+#include <math.h>
+#include "convex.h"
+
+void convex_builder_initialize(convex_builder_state_t* b, float epsilon)
+{
+       b->numcorners = 0;
+       b->numfaces = 0;
+       b->epsilon = 0.0f;
+}
+
+// this is a variant of QuickHull that relies on the caller to provide points
+// in a reasonable order - the result will be the same regardless of point order
+// but it's more efficient if the furthest points are provided first
+//
+// this could be a little more efficient if we kept track of edges during the
+// build, but I think it may be more numerically stable this way
+void convex_builder_add_point(convex_builder_state_t* b, float x, float y, float z)
+{
+       int i, j, l;
+       convex_corner_t corner;
+       unsigned char removedcorner[CONVEX_MAX_CORNERS];
+       unsigned char removedface[CONVEX_MAX_FACES];
+
+       // we can't add any new points after max generations is reached
+       if (b->numcorners > CONVEX_MAX_CORNERS - 1 || b->numfaces > CONVEX_MAX_FACES - b->numcorners - 2)
+               return;
+
+       // make a corner struct with the same layout we expect to use for vector ops
+       corner.x = x;
+       corner.y = y;
+       corner.z = z;
+       corner.w = 1.0f;
+
+       float epsilon = b->epsilon;
+
+       // add the new corner to the bounding box
+       if (b->numcorners == 0)
+       {
+               b->extents[0][0] = corner.x;
+               b->extents[0][1] = corner.y;
+               b->extents[0][2] = corner.z;
+               b->extents[1][0] = corner.x;
+               b->extents[1][1] = corner.y;
+               b->extents[1][2] = corner.z;
+       }
+       else
+       {
+               if (b->extents[0][0] > corner.x)
+                       b->extents[0][0] = corner.x;
+               if (b->extents[0][1] > corner.y)
+                       b->extents[0][1] = corner.y;
+               if (b->extents[0][2] > corner.z)
+                       b->extents[0][2] = corner.z;
+               if (b->extents[1][0] < corner.x)
+                       b->extents[1][0] = corner.x;
+               if (b->extents[1][1] < corner.y)
+                       b->extents[1][1] = corner.y;
+               if (b->extents[1][2] < corner.z)
+                       b->extents[1][2] = corner.z;
+       }
+
+       if (b->numfaces > 0)
+       {
+               // determine which faces will be inside the resulting solid
+               for (i = 0; i < b->numfaces; i++)
+               {
+                       convex_face_t* f = b->faces + i;
+                       // face will be removed if it places this corner outside the solid
+                       removedface[i] = (f->x * corner.x + f->y * corner.y + f->z * corner.z + f->w * corner.w) > epsilon;
+               }
+
+               // scan for removed faces
+               for (i = 0; i < b->numfaces; i++)
+                       if (removedface[i])
+                               break;
+
+               // exit early if point is completely inside the solid
+               if (i == b->numfaces)
+                       return;
+
+               // garbage collect the removed faces
+               for (j = i + 1; j < b->numfaces; j++)
+                       if (!removedface[j])
+                               b->faces[i++] = b->faces[j];
+               b->numfaces = i;
+       }
+
+       // iterate active corners to create replacement faces using the new corner
+       for (i = 0; i < b->numcorners; i++)
+       {
+               convex_corner_t ca = b->corners[i];
+               for (j = 0; j < b->numcorners; j++)
+               {
+                       // using the same point twice would make a degenerate plane
+                       if (i == j)
+                               continue;
+                       convex_corner_t cb = b->corners[j];
+                       // calculate the edge directions
+                       convex_corner_t d, e;
+                       convex_face_t face;
+                       d.x = ca.x - cb.x;
+                       d.y = ca.y - cb.y;
+                       d.z = ca.z - cb.z;
+                       d.w = 0.0f;
+                       e.x = corner.x - cb.x;
+                       e.y = corner.y - cb.y;
+                       e.z = corner.z - cb.z;
+                       e.w = 0.0f;
+                       // cross product to produce a normal; this is not unit length,
+                       // its length is the volume of the triangle *2
+                       face.x = d.y * e.z - d.z * e.y;
+                       face.y = d.z * e.x - d.x * e.z;
+                       face.z = d.x * e.y - d.y * e.x;
+                       float len2 = face.x * face.x + face.y * face.y + face.z * face.z;
+                       if (len2 == 0.0f)
+                       {
+                               // we can't do anything with a degenerate plane
+                               continue;
+                       }
+                       // normalize the plane normal
+                       float inv = 1.0f / sqrt(len2);
+                       face.x *= inv;
+                       face.y *= inv;
+                       face.z *= inv;
+                       face.w = -(corner.x * face.x + corner.y * face.y + corner.z * face.z);
+                       // flip the face if it's backwards (not facing center)
+                       if ((b->extents[0][0] + b->extents[1][0]) * 0.5f * face.x + (b->extents[0][1] + b->extents[1][1]) * 0.5f * face.y + (b->extents[0][2] + b->extents[1][2]) * 0.5f * face.z + face.w > 0.0f)
+                       {
+                               face.x *= -1.0f;
+                               face.y *= -1.0f;
+                               face.z *= -1.0f;
+                               face.w *= -1.0f;
+                       }
+                       // discard the proposed face if it slices through the solid
+                       for (l = 0; l < b->numcorners; l++)
+                       {
+                               convex_corner_t cl = b->corners[l];
+                               if (cl.x * face.x + cl.y * face.y + cl.z * face.z + face.w > epsilon)
+                                       break;
+                       }
+                       if (l < b->numcorners)
+                               continue;
+                       // add the new face
+                       b->faces[b->numfaces++] = face;
+               }
+       }
+
+       // discard any corners that are no longer on the surface of the solid
+       for (i = 0; i < b->numcorners; i++)
+       {
+               convex_corner_t ca = b->corners[i];
+               for (j = 0; j < b->numfaces; j++)
+               {
+                       const convex_face_t *f = b->faces + j;
+                       if (ca.x * f->x + ca.y * f->y + ca.z * f->z + ca.w * f->w > -epsilon)
+                               break;
+               }
+               // if we didn't find any face that uses this corner, remove the corner
+               removedcorner[i] = (j == b->numfaces);
+       }
+
+       // scan for removed corners and remove them
+       for (i = 0; i < b->numcorners; i++)
+               if (removedcorner[i])
+                       break;
+       for (j = i + 1;j < b->numcorners;j++)
+               if (!removedcorner[j])
+                       b->corners[i++] = b->corners[j];
+       b->numcorners = i;
+
+       // add the new corner
+       b->corners[b->numcorners++] = corner;
+}
+
+int convex_builder_get_planes4f(convex_builder_state_t* b, float* outplanes4f, int maxplanes, int positivew)
+{
+       int i;
+       int n = b->numfaces < maxplanes ? b->numfaces : maxplanes;
+       if (positivew)
+       {
+               for (i = 0; i < n; i++)
+               {
+                       const convex_face_t* f = b->faces + i;
+                       outplanes4f[i * 4 + 0] = f->x;
+                       outplanes4f[i * 4 + 1] = f->y;
+                       outplanes4f[i * 4 + 2] = f->z;
+                       outplanes4f[i * 4 + 3] = f->w * -1.0f;
+               }
+       }
+       else
+       {
+               for (i = 0; i < n; i++)
+               {
+                       const convex_face_t* f = b->faces + i;
+                       outplanes4f[i * 4 + 0] = f->x;
+                       outplanes4f[i * 4 + 1] = f->y;
+                       outplanes4f[i * 4 + 2] = f->z;
+                       outplanes4f[i * 4 + 3] = f->w;
+               }
+       }
+       return b->numfaces;
+}
+
+int convex_builder_get_points3f(convex_builder_state_t *b, float* outpoints3f, int maxpoints)
+{
+       int i;
+       int n = b->numcorners < maxpoints ? b->numcorners : maxpoints;
+       for (i = 0; i < n; i++)
+       {
+               const convex_corner_t* c = b->corners + i;
+               outpoints3f[i * 3 + 0] = c->x;
+               outpoints3f[i * 3 + 1] = c->y;
+               outpoints3f[i * 3 + 2] = c->z;
+       }
+       return b->numcorners;
+}
diff --git a/convex.h b/convex.h
new file mode 100644 (file)
index 0000000..f8881c8
--- /dev/null
+++ b/convex.h
@@ -0,0 +1,96 @@
+/*
+Copyright (c) 2022 Ashley Rose Hale (LadyHavoc)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+// This module is a variant of the QuickHull algorithm intended to create hulls
+// (brushes, aka n-sided polytopes or hulls) from a series of points provided by
+// the caller
+
+#pragma once
+
+#ifndef CONVEX_H
+
+enum convex_enums
+{
+       CONVEX_MAX_CORNERS = 256,
+       CONVEX_MAX_FACES = 1024,
+};
+
+typedef struct convex_corner_s
+{
+       float x;
+       float y;
+       float z;
+       float w; // 1.0f
+}
+convex_corner_t;
+
+typedef struct convex_face_s
+{
+       // plane equation: a * x + b * y + c * z + d * w = 0.0f
+       float x;
+       float y;
+       float z;
+       float w;
+}
+convex_face_t;
+
+typedef struct convex_builder_state_s
+{
+       // axially aligned bounding box
+       float extents[2][3];
+
+       int numcorners;
+       convex_corner_t corners[CONVEX_MAX_CORNERS];
+
+       int numfaces;
+       convex_face_t faces[CONVEX_MAX_FACES];
+
+       // we consider points to be equivalent if they are within this distance
+       // suggested value is maxextent / 1048576.0f, which is a way of saying 
+       // 'equivalent within 20 bits of precision'
+       float epsilon;
+}
+convex_builder_state_t;
+
+// set up a builer state to receive points
+void convex_builder_initialize(convex_builder_state_t* b, float epsilon);
+
+// this is a variant of QuickHull that relies on the caller to provide points
+// in a reasonable order - the result will be the same regardless of point order
+// but it's more efficient if the furthest points are provided first
+//
+// this could be a little more efficient if we kept track of edges during the
+// build, but I think it may be more numerically stable this way
+void convex_builder_add_point(convex_builder_state_t* b, float x, float y, float z);
+
+// returns computed faces in array of vec4
+// positivew=0 is for plane equations of the form a*x+b*y+c*z+w, which is the
+// internal format
+// positivew=1 is for plane equations of the form a*x+b*y+c*z-w, which tend to
+// be less friendly in terms of vector ops
+int convex_builder_get_planes4f(convex_builder_state_t* b, float* outplanes4f, int maxplanes, int positivew);
+
+// returns the points as an array of vec3
+// internal format is vec4, so this is just repacking the data
+int convex_builder_get_points3f(convex_builder_state_t* b, float* outpoints3f, int maxpoints);
+
+#endif
index 21b70954fc9045e163faf138591ece7bc0828051..501bef0be7d15400b602c17f86308db4ec1e41c9 100644 (file)
--- a/crypto.c
+++ b/crypto.c
@@ -750,9 +750,9 @@ qbool Crypto_RetrieveHostKey(lhnetaddress_t *peeraddress, int *keyid, char *keyf
        if(keyid)
                *keyid = hk->keyid;
        if(keyfp)
-               strlcpy(keyfp, pubkeys_fp64[hk->keyid], keyfplen);
+               dp_strlcpy(keyfp, pubkeys_fp64[hk->keyid], keyfplen);
        if(idfp)
-               strlcpy(idfp, hk->idfp, idfplen);
+               dp_strlcpy(idfp, hk->idfp, idfplen);
        if(aeslevel)
                *aeslevel = hk->aeslevel;
        if(issigned)
@@ -771,10 +771,10 @@ int Crypto_RetrieveLocalKey(int keyid, char *keyfp, size_t keyfplen, char *idfp,
        if(!pubkeys[keyid])
                return -1;
        if(keyfp)
-               strlcpy(keyfp, pubkeys_fp64[keyid], keyfplen);
+               dp_strlcpy(keyfp, pubkeys_fp64[keyid], keyfplen);
        if(idfp)
                if(pubkeys_havepriv[keyid])
-                       strlcpy(idfp, pubkeys_priv_fp64[keyid], idfplen);
+                       dp_strlcpy(idfp, pubkeys_priv_fp64[keyid], idfplen);
        if(issigned)
                *issigned = pubkeys_havesig[keyid];
        return 1;
@@ -848,7 +848,7 @@ static void Crypto_BuildIdString(void)
        dpsnprintf(crypto_idstring_buf, sizeof(crypto_idstring_buf), "%d", d0_rijndael_dll ? crypto_aeslevel.integer : 0);
        for (i = 0; i < MAX_PUBKEYS; ++i)
                if (pubkeys[i])
-                       strlcat(crypto_idstring_buf, va(vabuf, sizeof(vabuf), " %s@%s%s", pubkeys_priv_fp64[i], pubkeys_havesig[i] ? "" : "~", pubkeys_fp64[i]), sizeof(crypto_idstring_buf));
+                       dp_strlcat(crypto_idstring_buf, va(vabuf, sizeof(vabuf), " %s@%s%s", pubkeys_priv_fp64[i], pubkeys_havesig[i] ? "" : "~", pubkeys_fp64[i]), sizeof(crypto_idstring_buf));
        crypto_idstring = crypto_idstring_buf;
 }
 
@@ -1141,11 +1141,11 @@ static void Crypto_KeyGen_Finished(int code, size_t length_received, unsigned ch
        {
                if(length_received >= 5 && Crypto_LittleLong((const char *) buffer) == FOURCC_D0ER)
                {
-                       Con_Printf("Error response from keygen server: %.*s\n", (int)(length_received - 5), buffer + 5);
+                       Con_Printf(CON_ERROR "Error response from keygen server: %.*s\n", (int)(length_received - 5), buffer + 5);
                }
                else
                {
-                       Con_Printf("Invalid response from keygen server:\n");
+                       Con_Printf(CON_ERROR "Invalid response from keygen server:\n");
                        Com_HexDumpToConsole(buffer, (int)length_received);
                }
                keygen_i = -1;
@@ -1292,7 +1292,7 @@ static void Crypto_KeyGen_f(cmd_state_t *cmd)
                {
                        Con_Printf("Generated private ID key_%d.d0pk (public key fingerprint: %s)\n", keygen_i, pubkeys_priv_fp64[keygen_i]);
                        pubkeys_havepriv[keygen_i] = true;
-                       strlcat(crypto_idstring_buf, va(vabuf, sizeof(vabuf), " %s@%s", pubkeys_priv_fp64[keygen_i], pubkeys_fp64[keygen_i]), sizeof(crypto_idstring_buf));
+                       dp_strlcat(crypto_idstring_buf, va(vabuf, sizeof(vabuf), " %s@%s", pubkeys_priv_fp64[keygen_i], pubkeys_fp64[keygen_i]), sizeof(crypto_idstring_buf));
                        crypto_idstring = crypto_idstring_buf;
                        Crypto_BuildChallengeAppend();
                }
@@ -1741,16 +1741,15 @@ static int Crypto_ServerParsePacket_Internal(const char *data_in, size_t len_in,
 
        if (len_in > 8 && !memcmp(string, "connect\\", 8) && d0_rijndael_dll && crypto_aeslevel.integer >= 3)
        {
-               const char *s;
                int i;
                // sorry, we have to verify the challenge here to not reflect network spam
 
-               if (!(s = InfoString_GetValue(string + 4, "challenge", infostringvalue, sizeof(infostringvalue))))
+               if (!InfoString_GetValue(string + 4, "challenge", infostringvalue, sizeof(infostringvalue)))
                        return CRYPTO_NOMATCH; // will be later accepted if encryption was set up
                // validate the challenge
                for (i = 0;i < MAX_CHALLENGES;i++)
                        if(challenges[i].time > 0)
-                               if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strcmp(challenges[i].string, s))
+                               if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strcmp(challenges[i].string, infostringvalue))
                                        break;
                // if the challenge is not recognized, drop the packet
                if (i == MAX_CHALLENGES) // challenge mismatch is silent
@@ -1762,12 +1761,11 @@ static int Crypto_ServerParsePacket_Internal(const char *data_in, size_t len_in,
        }
        else if(len_in > 5 && !memcmp(string, "d0pk\\", 5) && ((LHNETADDRESS_GetAddressType(peeraddress) == LHNETADDRESSTYPE_LOOP) || sv_public.integer > -3))
        {
-               const char *cnt, *s, *p;
+               const char *cnt, *p;
                int id;
                int clientid = -1, serverid = -1;
-               cnt = InfoString_GetValue(string + 4, "id", infostringvalue, sizeof(infostringvalue));
-               id = (cnt ? atoi(cnt) : -1);
-               cnt = InfoString_GetValue(string + 4, "cnt", infostringvalue, sizeof(infostringvalue));
+               id = (InfoString_GetValue(string + 4, "id", infostringvalue, sizeof(infostringvalue)) ? atoi(infostringvalue) : -1);
+               cnt = (InfoString_GetValue(string + 4, "cnt", infostringvalue, sizeof(infostringvalue)) ? infostringvalue : NULL);
                if(!cnt)
                        return Crypto_SoftServerError(data_out, len_out, "missing cnt in d0pk");
                GetUntilNul(&data_in, &len_in);
@@ -1776,21 +1774,21 @@ static int Crypto_ServerParsePacket_Internal(const char *data_in, size_t len_in,
                if(!strcmp(cnt, "0"))
                {
                        int i;
-                       if (!(s = InfoString_GetValue(string + 4, "challenge", infostringvalue, sizeof(infostringvalue))))
+                       if (!InfoString_GetValue(string + 4, "challenge", infostringvalue, sizeof(infostringvalue)))
                                return Crypto_SoftServerError(data_out, len_out, "missing challenge in d0pk\\0");
                        // validate the challenge
                        for (i = 0;i < MAX_CHALLENGES;i++)
                                if(challenges[i].time > 0)
-                                       if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strcmp(challenges[i].string, s))
+                                       if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strcmp(challenges[i].string, infostringvalue))
                                                break;
                        // if the challenge is not recognized, drop the packet
                        if (i == MAX_CHALLENGES)
                                return Crypto_SoftServerError(data_out, len_out, "invalid challenge in d0pk\\0");
 
-                       if (!(s = InfoString_GetValue(string + 4, "aeslevel", infostringvalue, sizeof(infostringvalue))))
+                       if (!InfoString_GetValue(string + 4, "aeslevel", infostringvalue, sizeof(infostringvalue)))
                                aeslevel = 0; // not supported
                        else
-                               aeslevel = bound(0, atoi(s), 3);
+                               aeslevel = bound(0, atoi(infostringvalue), 3);
                        switch(bound(0, d0_rijndael_dll ? crypto_aeslevel.integer : 0, 3))
                        {
                                default: // dummy, never happens, but to make gcc happy...
@@ -1858,8 +1856,8 @@ static int Crypto_ServerParsePacket_Internal(const char *data_in, size_t len_in,
                        if(CDATA->s >= 0)
                        {
                                // I am the server, and my key is ok... so let's set server_keyfp and server_idfp
-                               strlcpy(crypto->server_keyfp, pubkeys_fp64[CDATA->s], sizeof(crypto->server_keyfp));
-                               strlcpy(crypto->server_idfp, pubkeys_priv_fp64[CDATA->s], sizeof(crypto->server_idfp));
+                               dp_strlcpy(crypto->server_keyfp, pubkeys_fp64[CDATA->s], sizeof(crypto->server_keyfp));
+                               dp_strlcpy(crypto->server_idfp, pubkeys_priv_fp64[CDATA->s], sizeof(crypto->server_idfp));
                                crypto->server_issigned = pubkeys_havesig[CDATA->s];
 
                                if(!CDATA->id)
@@ -2001,7 +1999,7 @@ static int Crypto_ServerParsePacket_Internal(const char *data_in, size_t len_in,
                                CLEAR_CDATA;
                                return Crypto_ServerError(data_out, len_out, "d0_blind_id_authenticate_with_private_id_verify failed (authentication error)", "Authentication error");
                        }
-                       strlcpy(crypto->client_keyfp, pubkeys_fp64[CDATA->c], sizeof(crypto->client_keyfp));
+                       dp_strlcpy(crypto->client_keyfp, pubkeys_fp64[CDATA->c], sizeof(crypto->client_keyfp));
                        crypto->client_issigned = status;
 
                        memset(crypto->client_idfp, 0, sizeof(crypto->client_idfp));
@@ -2048,7 +2046,7 @@ int Crypto_ServerParsePacket(const char *data_in, size_t len_in, char *data_out,
                if(len_in > 5 && !memcmp(data_in, "d0pk\\", 5))
                {
                        do_time = true;
-                       cnt = InfoString_GetValue(data_in + 4, "cnt", infostringvalue, sizeof(infostringvalue));
+                       cnt = (InfoString_GetValue(data_in + 4, "cnt", infostringvalue, sizeof(infostringvalue)) ? infostringvalue : NULL);
                        if(cnt)
                                if(!strcmp(cnt, "0"))
                                        do_reject = true;
@@ -2106,11 +2104,10 @@ static int Crypto_SoftClientError(char *data_out, size_t *len_out, const char *m
        return CRYPTO_DISCARD;
 }
 
-int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, size_t *len_out, lhnetaddress_t *peeraddress)
+int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, size_t *len_out, lhnetaddress_t *peeraddress, const char *peeraddressstring)
 {
        crypto_t *crypto = &cls.crypto;
        const char *string = data_in;
-       const char *s;
        D0_BOOL aes;
        char *data_out_p = data_out;
        D0_BOOL status;
@@ -2173,9 +2170,8 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out,
        }
        else if (len_in >= 13 && !memcmp(string, "infoResponse\x0A", 13))
        {
-               s = InfoString_GetValue(string + 13, "d0_blind_id", infostringvalue, sizeof(infostringvalue));
-               if(s)
-                       Crypto_StoreHostKey(peeraddress, s, true);
+               if(InfoString_GetValue(string + 13, "d0_blind_id", infostringvalue, sizeof(infostringvalue)))
+                       Crypto_StoreHostKey(peeraddress, infostringvalue, true);
                return CRYPTO_NOMATCH;
        }
        else if (len_in >= 15 && !memcmp(string, "statusResponse\x0A", 15))
@@ -2188,9 +2184,8 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out,
                        save = *p;
                        * (char *) p = 0; // cut off the string there
                }
-               s = InfoString_GetValue(string + 15, "d0_blind_id", infostringvalue, sizeof(infostringvalue));
-               if(s)
-                       Crypto_StoreHostKey(peeraddress, s, true);
+               if(InfoString_GetValue(string + 15, "d0_blind_id", infostringvalue, sizeof(infostringvalue)))
+                       Crypto_StoreHostKey(peeraddress, infostringvalue, true);
                if(p)
                {
                        * (char *) p = save;
@@ -2214,7 +2209,12 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out,
 
                // Must check the source IP here, if we want to prevent other servers' replies from falsely advancing the crypto state, preventing successful connect to the real server.
                if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address))
-                       return Crypto_SoftClientError(data_out, len_out, "challenge message from wrong server");
+               {
+                       char warn_msg[128];
+
+                       dpsnprintf(warn_msg, sizeof(warn_msg), "ignoring challenge message from wrong server %s", peeraddressstring);
+                       return Crypto_SoftClientError(data_out, len_out, warn_msg);
+               }
 
                // if we have a stored host key for the server, assume serverid to already be selected!
                // (the loop will refuse to overwrite this one then)
@@ -2319,7 +2319,7 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out,
                        CDATA->s = serverid;
                        CDATA->c = clientid;
                        memset(crypto->dhkey, 0, sizeof(crypto->dhkey));
-                       strlcpy(CDATA->challenge, challenge, sizeof(CDATA->challenge));
+                       dp_strlcpy(CDATA->challenge, challenge, sizeof(CDATA->challenge));
                        crypto->client_keyfp[0] = 0;
                        crypto->client_idfp[0] = 0;
                        crypto->server_keyfp[0] = 0;
@@ -2358,8 +2358,8 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out,
                        if(clientid >= 0)
                        {
                                // I am the client, and my key is ok... so let's set client_keyfp and client_idfp
-                               strlcpy(crypto->client_keyfp, pubkeys_fp64[CDATA->c], sizeof(crypto->client_keyfp));
-                               strlcpy(crypto->client_idfp, pubkeys_priv_fp64[CDATA->c], sizeof(crypto->client_idfp));
+                               dp_strlcpy(crypto->client_keyfp, pubkeys_fp64[CDATA->c], sizeof(crypto->client_keyfp));
+                               dp_strlcpy(crypto->client_idfp, pubkeys_priv_fp64[CDATA->c], sizeof(crypto->client_idfp));
                                crypto->client_issigned = pubkeys_havesig[CDATA->c];
                        }
 
@@ -2422,11 +2422,15 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out,
 
                // Must check the source IP here, if we want to prevent other servers' replies from falsely advancing the crypto state, preventing successful connect to the real server.
                if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address))
-                       return Crypto_SoftClientError(data_out, len_out, "d0pk\\ message from wrong server");
+               {
+                       char warn_msg[128];
+
+                       dpsnprintf(warn_msg, sizeof(warn_msg), "ignoring d0pk\\ message from wrong server %s", peeraddressstring);
+                       return Crypto_SoftClientError(data_out, len_out, warn_msg);
+               }
 
-               cnt = InfoString_GetValue(string + 4, "id", infostringvalue, sizeof(infostringvalue));
-               id = (cnt ? atoi(cnt) : -1);
-               cnt = InfoString_GetValue(string + 4, "cnt", infostringvalue, sizeof(infostringvalue));
+               id = (InfoString_GetValue(string + 4, "id", infostringvalue, sizeof(infostringvalue)) ? atoi(infostringvalue) : -1);
+               cnt = (InfoString_GetValue(string + 4, "cnt", infostringvalue, sizeof(infostringvalue)) ? infostringvalue : NULL);
                if(!cnt)
                        return Crypto_ClientError(data_out, len_out, "d0pk\\ message without cnt");
                GetUntilNul(&data_in, &len_in);
@@ -2443,8 +2447,8 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out,
 
                        cls.connect_nextsendtime = max(cls.connect_nextsendtime, host.realtime + 1); // prevent "hammering"
 
-                       if((s = InfoString_GetValue(string + 4, "aes", infostringvalue, sizeof(infostringvalue))))
-                               aes = atoi(s);
+                       if(InfoString_GetValue(string + 4, "aes", infostringvalue, sizeof(infostringvalue)))
+                               aes = atoi(infostringvalue);
                        else
                                aes = false;
                        // we CANNOT toggle the AES status any more!
@@ -2498,7 +2502,7 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out,
                                return Crypto_ClientError(data_out, len_out, "d0_blind_id_authenticate_with_private_id_verify failed (server authentication error)");
                        }
 
-                       strlcpy(crypto->server_keyfp, pubkeys_fp64[CDATA->s], sizeof(crypto->server_keyfp));
+                       dp_strlcpy(crypto->server_keyfp, pubkeys_fp64[CDATA->s], sizeof(crypto->server_keyfp));
                        if (!status && CDATA->wantserver_issigned)
                        {
                                CLEAR_CDATA;
@@ -2576,8 +2580,8 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out,
 
                        if(CDATA->s < 0) // only if server didn't auth
                        {
-                               if((s = InfoString_GetValue(string + 4, "aes", infostringvalue, sizeof(infostringvalue))))
-                                       aes = atoi(s);
+                               if(InfoString_GetValue(string + 4, "aes", infostringvalue, sizeof(infostringvalue)))
+                                       aes = atoi(infostringvalue);
                                else
                                        aes = false;
                                if(CDATA->wantserver_idfp[0]) // if we know a host key, honor its encryption setting
index 306134de96eeb2bb1c743e9cdd50dac05b72b0ea..bbb4ea1fa4526513eca80f838c18070df625b35b 100644 (file)
--- a/crypto.h
+++ b/crypto.h
@@ -65,8 +65,8 @@ const void *Crypto_DecryptPacket(crypto_t *crypto, const void *data_src, size_t
 #define CRYPTO_MATCH 1          // process as usual (packet was used)
 #define CRYPTO_DISCARD 2        // discard this packet
 #define CRYPTO_REPLACE 3        // make the buffer the current packet
-int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, size_t *len_out, struct lhnetaddress_s *peeraddress);
-int Crypto_ServerParsePacket(const char *data_in, size_t len_in, char *data_out, size_t *len_out, struct lhnetaddress_s *peeraddress);
+int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, size_t *len_out, lhnetaddress_t *peeraddress, const char *peeraddressstring);
+int Crypto_ServerParsePacket(const char *data_in, size_t len_in, char *data_out, size_t *len_out, lhnetaddress_t *peeraddress);
 
 // if len_out is nonzero, the packet is to be sent to the client
 
index 4bacb5632e8a595d00d98ed5962766a128b7a67a..d9d8630ac3a08cb69d160179646be9aac9a2fa06 100644 (file)
--- a/csprogs.c
+++ b/csprogs.c
@@ -32,30 +32,34 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 //[515]: omg !!! optimize it ! a lot of hacks here and there also :P
 
 #define CSQC_RETURNVAL prog->globals.fp[OFS_RETURN]
-#define CSQC_BEGIN
-#define CSQC_END
 
 void CL_VM_PreventInformationLeaks(void)
 {
        prvm_prog_t *prog = CLVM_prog;
-       if(!cl.csqc_loaded)
+
+       if(!prog->loaded)
                return;
-CSQC_BEGIN
+
        VM_ClearTraceGlobals(prog);
        PRVM_clientglobalfloat(trace_networkentity) = 0;
-CSQC_END
 }
 
-//[515]: these are required funcs
-static const char *cl_required_func[] =
-{
-       "CSQC_Init",
-       "CSQC_InputEvent",
-       "CSQC_UpdateView",
-       "CSQC_ConsoleCommand",
-};
 
-static int cl_numrequiredfunc = sizeof(cl_required_func) / sizeof(char*);
+/** Previous DP versions declined to load csprogs if it lacked any of:
+ * CSQC_Init, CSQC_InputEvent, CSQC_UpdateView, CSQC_ConsoleCommand
+ * whereas in FTE and QSS-based engines the minimum is either CSQC_UpdateView
+ * or CSQC_DrawHud (only called in CSQC_SIMPLE aka hud-only mode)
+ * and the other funcs are optional, so we now behave the same here.
+ */
+static void CL_CheckRequiredFuncs(prvm_prog_t *prog, const char *filename)
+{
+       if (PRVM_ED_FindFunction(prog, "CSQC_UpdateView"))
+               return;
+       else if (PRVM_ED_FindFunction(prog, "CSQC_DrawHud"))
+               prog->flag |= PRVM_CSQC_SIMPLE;
+       else
+               prog->error_cmd("%s: no CSQC_UpdateView (EXT_CSQC) or CSQC_DrawHud (CSQC_SIMPLE) function found in %s", prog->name, filename);
+}
 
 #define CL_REQFIELDS (sizeof(cl_reqfields) / sizeof(prvm_required_field_t))
 
@@ -218,26 +222,25 @@ prvm_required_field_t cl_reqglobals[] =
 void CL_VM_UpdateDmgGlobals (int dmg_take, int dmg_save, vec3_t dmg_origin)
 {
        prvm_prog_t *prog = CLVM_prog;
-       if(cl.csqc_loaded)
+
+       if(prog->loaded)
        {
-               CSQC_BEGIN
                PRVM_clientglobalfloat(dmg_take) = dmg_take;
                PRVM_clientglobalfloat(dmg_save) = dmg_save;
                VectorCopy(dmg_origin, PRVM_clientglobalvector(dmg_origin));
-               CSQC_END
        }
 }
 
 void CSQC_UpdateNetworkTimes(double newtime, double oldtime)
 {
        prvm_prog_t *prog = CLVM_prog;
-       if(!cl.csqc_loaded)
+
+       if(!prog->loaded)
                return;
-       CSQC_BEGIN
+
        PRVM_clientglobalfloat(servertime) = newtime;
        PRVM_clientglobalfloat(serverprevtime) = oldtime;
        PRVM_clientglobalfloat(serverdeltatime) = newtime - oldtime;
-       CSQC_END
 }
 
 //[515]: set globals before calling R_UpdateView, WEIRD CRAP
@@ -245,37 +248,36 @@ static void CSQC_SetGlobals (double frametime)
 {
        vec3_t pmove_org;
        prvm_prog_t *prog = CLVM_prog;
-       CSQC_BEGIN
-               PRVM_clientglobalfloat(time) = cl.time;
-               PRVM_clientglobalfloat(cltime) = host.realtime; // Spike named it that way.
-               PRVM_clientglobalfloat(frametime) = frametime;
-               PRVM_clientglobalfloat(servercommandframe) = cls.servermovesequence;
-               PRVM_clientglobalfloat(clientcommandframe) = cl.movecmd[0].sequence;
-               VectorCopy(cl.viewangles, PRVM_clientglobalvector(input_angles));
-               // // FIXME: this actually belongs into getinputstate().. [12/17/2007 Black]
-               PRVM_clientglobalfloat(input_buttons) = cl.movecmd[0].buttons;
-               VectorSet(PRVM_clientglobalvector(input_movevalues), cl.movecmd[0].forwardmove, cl.movecmd[0].sidemove, cl.movecmd[0].upmove);
-               VectorCopy(cl.csqc_vieworiginfromengine, cl.csqc_vieworigin);
-               VectorCopy(cl.csqc_viewanglesfromengine, cl.csqc_viewangles);
-
-               // LadyHavoc: Spike says not to do this, but without pmove_org the
-               // CSQC is useless as it can't alter the view origin without
-               // completely replacing it
-               Matrix4x4_OriginFromMatrix(&cl.entities[cl.viewentity].render.matrix, pmove_org);
-               VectorCopy(pmove_org, PRVM_clientglobalvector(pmove_org));
-               VectorCopy(cl.movement_velocity, PRVM_clientglobalvector(pmove_vel));
-               PRVM_clientglobalfloat(pmove_onground) = cl.onground;
-               PRVM_clientglobalfloat(pmove_inwater) = cl.inwater;
-
-               VectorCopy(cl.viewangles, PRVM_clientglobalvector(view_angles));
-               VectorCopy(cl.punchangle, PRVM_clientglobalvector(view_punchangle));
-               VectorCopy(cl.punchvector, PRVM_clientglobalvector(view_punchvector));
-               PRVM_clientglobalfloat(maxclients) = cl.maxclients;
-
-               PRVM_clientglobalfloat(player_localentnum) = cl.viewentity;
-
-               CSQC_R_RecalcView();
-       CSQC_END
+
+       PRVM_clientglobalfloat(time) = cl.time;
+       PRVM_clientglobalfloat(cltime) = host.realtime; // Spike named it that way.
+       PRVM_clientglobalfloat(frametime) = frametime;
+       PRVM_clientglobalfloat(servercommandframe) = cls.servermovesequence;
+       PRVM_clientglobalfloat(clientcommandframe) = cl.movecmd[0].sequence;
+       VectorCopy(cl.viewangles, PRVM_clientglobalvector(input_angles));
+       // // FIXME: this actually belongs into getinputstate().. [12/17/2007 Black]
+       PRVM_clientglobalfloat(input_buttons) = cl.movecmd[0].buttons;
+       VectorSet(PRVM_clientglobalvector(input_movevalues), cl.movecmd[0].forwardmove, cl.movecmd[0].sidemove, cl.movecmd[0].upmove);
+       VectorCopy(cl.csqc_vieworiginfromengine, cl.csqc_vieworigin);
+       VectorCopy(cl.csqc_viewanglesfromengine, cl.csqc_viewangles);
+
+       // LadyHavoc: Spike says not to do this, but without pmove_org the
+       // CSQC is useless as it can't alter the view origin without
+       // completely replacing it
+       Matrix4x4_OriginFromMatrix(&cl.entities[cl.viewentity].render.matrix, pmove_org);
+       VectorCopy(pmove_org, PRVM_clientglobalvector(pmove_org));
+       VectorCopy(cl.movement_velocity, PRVM_clientglobalvector(pmove_vel));
+       PRVM_clientglobalfloat(pmove_onground) = cl.onground;
+       PRVM_clientglobalfloat(pmove_inwater) = cl.inwater;
+
+       VectorCopy(cl.viewangles, PRVM_clientglobalvector(view_angles));
+       VectorCopy(cl.punchangle, PRVM_clientglobalvector(view_punchangle));
+       VectorCopy(cl.punchvector, PRVM_clientglobalvector(view_punchvector));
+       PRVM_clientglobalfloat(maxclients) = cl.maxclients;
+
+       PRVM_clientglobalfloat(player_localentnum) = cl.viewentity;
+
+       CSQC_R_RecalcView();
 }
 
 void CSQC_Predraw (prvm_edict_t *ed)
@@ -459,10 +461,9 @@ qbool CL_VM_InputEvent (int eventtype, float x, float y)
        prvm_prog_t *prog = CLVM_prog;
        qbool r;
 
-       if(!cl.csqc_loaded)
+       if(!prog->loaded)
                return false;
 
-CSQC_BEGIN
        if (!PRVM_clientfunction(CSQC_InputEvent))
                r = false;
        else
@@ -475,7 +476,6 @@ CSQC_BEGIN
                prog->ExecuteProgram(prog, PRVM_clientfunction(CSQC_InputEvent), "QC function CSQC_InputEvent is missing");
                r = CSQC_RETURNVAL != 0;
        }
-CSQC_END
        return r;
 }
 
@@ -489,60 +489,94 @@ qbool CL_VM_UpdateView (double frametime)
        emptyvector[1] = 0;
        emptyvector[2] = 0;
 //     vec3_t oldangles;
-       if(!cl.csqc_loaded)
+
+       if(!prog->loaded)
                return false;
+
        R_TimeReport("pre-UpdateView");
-       CSQC_BEGIN
-               csqc_original_r_refdef_view = r_refdef.view;
-               csqc_main_r_refdef_view = r_refdef.view;
-               //VectorCopy(cl.viewangles, oldangles);
-               PRVM_clientglobalfloat(time) = cl.time;
-               PRVM_clientglobaledict(self) = cl.csqc_server2csqcentitynumber[cl.playerentity];
-               CSQC_SetGlobals(frametime);
-               // clear renderable entity and light lists to prevent crashes if the
-               // CSQC_UpdateView function does not call R_ClearScene as it should
-               r_refdef.scene.numentities = 0;
-               r_refdef.scene.numlights = 0;
-               // polygonbegin without draw2d arg has to guess
-               prog->polygonbegin_guess2d = false;
-               // free memory for resources that are no longer referenced
-               PRVM_GarbageCollection(prog);
-               // pass in width and height and menu/focus state as parameters (EXT_CSQC_1)
-               PRVM_G_FLOAT(OFS_PARM0) = vid.width;
-               PRVM_G_FLOAT(OFS_PARM1) = vid.height;
-               /*
-                * This should be fine for now but FTEQW uses flags for keydest
-                * and checks that an array called "eyeoffset" is 0
-                * 
-                * Just a note in case there's compatibility problems later
-                */
-               PRVM_G_FLOAT(OFS_PARM2) = key_dest == key_game;
-               prog->ExecuteProgram(prog, PRVM_clientfunction(CSQC_UpdateView), "QC function CSQC_UpdateView is missing");
-               //VectorCopy(oldangles, cl.viewangles);
-               // Dresk : Reset Dmg Globals Here
-               CL_VM_UpdateDmgGlobals(0, 0, emptyvector);
-               r_refdef.view = csqc_main_r_refdef_view;
-               R_RenderView_UpdateViewVectors(); // we have to do this, as we undid the scene render doing this for us
-       CSQC_END
+
+       csqc_original_r_refdef_view = r_refdef.view;
+       csqc_main_r_refdef_view = r_refdef.view;
+       //VectorCopy(cl.viewangles, oldangles);
+       PRVM_clientglobalfloat(time) = cl.time;
+       PRVM_clientglobaledict(self) = cl.csqc_server2csqcentitynumber[cl.playerentity];
+       CSQC_SetGlobals(frametime);
+       // clear renderable entity and light lists to prevent crashes if the
+       // CSQC_UpdateView function does not call R_ClearScene as it should
+       r_refdef.scene.numentities = 0;
+       r_refdef.scene.numlights = 0;
+       // polygonbegin without draw2d arg has to guess
+       prog->polygonbegin_guess2d = false;
+       // free memory for resources that are no longer referenced
+       PRVM_GarbageCollection(prog);
+       // pass in width and height and menu/focus state as parameters (EXT_CSQC_1)
+       PRVM_G_FLOAT(OFS_PARM0) = vid.width;
+       PRVM_G_FLOAT(OFS_PARM1) = vid.height;
+       /*
+        * This should be fine for now but FTEQW uses flags for keydest
+        * and checks that an array called "eyeoffset" is 0
+        *
+        * Just a note in case there's compatibility problems later
+        */
+       PRVM_G_FLOAT(OFS_PARM2) = key_dest == key_game;
+       prog->ExecuteProgram(prog, PRVM_clientfunction(CSQC_UpdateView), "QC function CSQC_UpdateView is missing");
+       //VectorCopy(oldangles, cl.viewangles);
+       // Dresk : Reset Dmg Globals Here
+       CL_VM_UpdateDmgGlobals(0, 0, emptyvector);
+       r_refdef.view = csqc_main_r_refdef_view;
+       R_RenderView_UpdateViewVectors(); // we have to do this, as we undid the scene render doing this for us
 
        R_TimeReport("UpdateView");
        return true;
 }
 
-qbool CL_VM_ConsoleCommand (const char *text)
+void CL_VM_DrawHud(double frametime)
 {
        prvm_prog_t *prog = CLVM_prog;
-       return PRVM_ConsoleCommand(prog, text, &prog->funcoffsets.CSQC_ConsoleCommand, false, cl.csqc_server2csqcentitynumber[cl.playerentity], cl.time, cl.csqc_loaded, "QC function CSQC_ConsoleCommand is missing");
+
+       R_TimeReport("pre-DrawHud");
+
+       PRVM_clientglobalfloat(time) = cl.time;
+       PRVM_clientglobaledict(self) = cl.csqc_server2csqcentitynumber[cl.playerentity];
+       CSQC_SetGlobals(frametime);
+
+       PRVM_GarbageCollection(prog);
+
+       // width and height parameters are virtual in CSQC_SIMPLE engines
+       VectorSet(PRVM_G_VECTOR(OFS_PARM0), vid_conwidth.integer, vid_conheight.integer, 0);
+       PRVM_G_FLOAT(OFS_PARM1) = sb_showscores;
+       prog->ExecuteProgram(prog, PRVM_clientfunction(CSQC_DrawHud), "QC function CSQC_DrawHud is missing");
+
+       if (PRVM_clientfunction(CSQC_DrawScores))
+       {
+               VectorSet(PRVM_G_VECTOR(OFS_PARM0), vid_conwidth.integer, vid_conheight.integer, 0);
+               PRVM_G_FLOAT(OFS_PARM1) = sb_showscores;
+               if (key_dest != key_menu)
+                       prog->ExecuteProgram(prog, PRVM_clientfunction(CSQC_DrawScores), "QC function CSQC_DrawScores is missing");
+       }
+       else if (sb_showscores || (cl.stats[STAT_HEALTH] <= 0 && cl_deathscoreboard.integer))
+               if (!cl.islocalgame) // LadyHavoc: changed to draw the deathmatch overlays in any multiplayer mode
+                       Sbar_DeathmatchOverlay ();
+
+       R_TimeReport("DrawHud");
+}
+
+
+qbool CL_VM_ConsoleCommand(const char *text, size_t textlen)
+{
+       prvm_prog_t *prog = CLVM_prog;
+       return PRVM_ConsoleCommand(prog, text, textlen, &prog->funcoffsets.CSQC_ConsoleCommand, false, cl.csqc_server2csqcentitynumber[cl.playerentity], cl.time, "QC function CSQC_ConsoleCommand is missing");
 }
 
 qbool CL_VM_Parse_TempEntity (void)
 {
        prvm_prog_t *prog = CLVM_prog;
-       int                     t;
-       qbool   r = false;
-       if(!cl.csqc_loaded)
+       int t;
+       qbool r = false;
+
+       if(!prog->loaded)
                return false;
-       CSQC_BEGIN
+
        if(PRVM_clientfunction(CSQC_Parse_TempEntity))
        {
                t = cl_message.readcount;
@@ -556,14 +590,14 @@ qbool CL_VM_Parse_TempEntity (void)
                        cl_message.badread = false;
                }
        }
-       CSQC_END
        return r;
 }
 
-void CL_VM_Parse_StuffCmd (const char *msg)
+void CL_VM_Parse_StuffCmd(const char *msg, size_t msg_len)
 {
        prvm_prog_t *prog = CLVM_prog;
        int restorevm_tempstringsbuf_cursize;
+
        if(msg[0] == 'c')
        if(msg[1] == 's')
        if(msg[2] == 'q')
@@ -575,164 +609,121 @@ void CL_VM_Parse_StuffCmd (const char *msg)
                int crcflags = csqc_progcrc.flags;
                csqc_progcrc.flags &= ~CF_READONLY;
                csqc_progsize.flags &= ~CF_READONLY;
-               Cmd_ExecuteString(cmd_local, msg, src_local, true);
+               Cmd_ExecuteString(cmd_local, msg, msg_len, src_local, true);
                csqc_progcrc.flags = csqc_progsize.flags = crcflags;
                return;
        }
 
-       if(cls.demoplayback)
-       if(!strncmp(msg, "curl --clear_autodownload\ncurl --pak --forthismap --as ", 55))
-       {
-               // special handling for map download commands
-               // run these commands IMMEDIATELY, instead of waiting for a client frame
-               // that way, there is no black screen when playing back demos
-               // I know this is a really ugly hack, but I can't think of any better way
-               // FIXME find the actual CAUSE of this, and make demo playback WAIT
-               // until all maps are loaded, then remove this hack
-
-               char buf[MAX_INPUTLINE];
-               const char *p, *q;
-               size_t l;
-
-               p = msg;
-
-               for(;;)
-               {
-                       q = strchr(p, '\n');
-                       if(q)
-                               l = q - p;
-                       else
-                               l = strlen(p);
-                       if(l > sizeof(buf) - 1)
-                               l = sizeof(buf) - 1;
-                       strlcpy(buf, p, l + 1); // strlcpy needs a + 1 as it includes the newline!
-
-                       Cmd_ExecuteString(cmd_local, buf, src_local, true);
-
-                       p += l;
-                       if(*p == '\n')
-                               ++p; // skip the newline and continue
-                       else
-                               break; // end of string or overflow
-               }
-               Cmd_ExecuteString(cmd_local, "curl --clear_autodownload", src_local, true); // don't inhibit CSQC loading
-               return;
-       }
-
-       if(!cl.csqc_loaded)
+       if(!prog->loaded)
        {
                Cbuf_AddText(cmd_local, msg);
                return;
        }
-       CSQC_BEGIN
+
        if(PRVM_clientfunction(CSQC_Parse_StuffCmd))
        {
                PRVM_clientglobalfloat(time) = cl.time;
                PRVM_clientglobaledict(self) = cl.csqc_server2csqcentitynumber[cl.playerentity];
                restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
-               PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(prog, msg);
+               PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(prog, msg, msg_len);
                prog->ExecuteProgram(prog, PRVM_clientfunction(CSQC_Parse_StuffCmd), "QC function CSQC_Parse_StuffCmd is missing");
                prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
        }
        else
                Cbuf_AddText(cmd_local, msg);
-       CSQC_END
 }
 
-static void CL_VM_Parse_Print (const char *msg)
+static void CL_VM_Parse_Print(const char *msg, size_t msg_len)
 {
        prvm_prog_t *prog = CLVM_prog;
        int restorevm_tempstringsbuf_cursize;
        PRVM_clientglobalfloat(time) = cl.time;
        PRVM_clientglobaledict(self) = cl.csqc_server2csqcentitynumber[cl.playerentity];
        restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
-       PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(prog, msg);
+       PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(prog, msg, msg_len);
        prog->ExecuteProgram(prog, PRVM_clientfunction(CSQC_Parse_Print), "QC function CSQC_Parse_Print is missing");
        prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
 }
 
-void CSQC_AddPrintText (const char *msg)
+void CSQC_AddPrintText(const char *msg, size_t msg_len)
 {
        prvm_prog_t *prog = CLVM_prog;
-       size_t i;
-       CSQC_BEGIN
-       if(cl.csqc_loaded && PRVM_clientfunction(CSQC_Parse_Print))
+       char *start = cl.csqc_printtextbuf + cl.csqc_printtextbuf_len;
+       size_t writebytes = min(msg_len + 1, MAX_INPUTLINE - cl.csqc_printtextbuf_len);
+
+       if(prog->loaded && PRVM_clientfunction(CSQC_Parse_Print))
        {
-               // FIXME: is this bugged?
-               i = strlen(msg)-1;
-               if(msg[i] != '\n' && msg[i] != '\r')
+               if(msg[msg_len - 1] != '\n' && msg[msg_len - 1] != '\r')
                {
-                       if(strlen(cl.csqc_printtextbuf)+i >= MAX_INPUTLINE)
+                       if(cl.csqc_printtextbuf_len + msg_len + 1 >= MAX_INPUTLINE)
                        {
-                               CL_VM_Parse_Print(cl.csqc_printtextbuf);
-                               cl.csqc_printtextbuf[0] = 0;
+                               CL_VM_Parse_Print(cl.csqc_printtextbuf, cl.csqc_printtextbuf_len);
+                               cl.csqc_printtextbuf[0] = '\0';
+                               cl.csqc_printtextbuf_len = 0;
                        }
                        else
-                               strlcat(cl.csqc_printtextbuf, msg, MAX_INPUTLINE);
+                       {
+                               memcpy(start, msg, writebytes);
+                               cl.csqc_printtextbuf_len += msg_len;
+                       }
                        return;
                }
-               strlcat(cl.csqc_printtextbuf, msg, MAX_INPUTLINE);
-               CL_VM_Parse_Print(cl.csqc_printtextbuf);
-               cl.csqc_printtextbuf[0] = 0;
+               memcpy(start, msg, writebytes);
+               cl.csqc_printtextbuf_len += msg_len;
+               CL_VM_Parse_Print(cl.csqc_printtextbuf, cl.csqc_printtextbuf_len);
+               cl.csqc_printtextbuf[0] = '\0';
+               cl.csqc_printtextbuf_len = 0;
        }
        else
                Con_Print(msg);
-       CSQC_END
 }
 
-void CL_VM_Parse_CenterPrint (const char *msg)
+void CL_VM_Parse_CenterPrint(const char *msg, size_t msg_len)
 {
        prvm_prog_t *prog = CLVM_prog;
        int restorevm_tempstringsbuf_cursize;
-       CSQC_BEGIN
-       if(cl.csqc_loaded && PRVM_clientfunction(CSQC_Parse_CenterPrint))
+
+       if(prog->loaded && PRVM_clientfunction(CSQC_Parse_CenterPrint))
        {
                PRVM_clientglobalfloat(time) = cl.time;
                PRVM_clientglobaledict(self) = cl.csqc_server2csqcentitynumber[cl.playerentity];
                restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
-               PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(prog, msg);
+               PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(prog, msg, msg_len);
                prog->ExecuteProgram(prog, PRVM_clientfunction(CSQC_Parse_CenterPrint), "QC function CSQC_Parse_CenterPrint is missing");
                prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
        }
        else
                SCR_CenterPrint(msg);
-       CSQC_END
 }
 
 void CL_VM_UpdateIntermissionState (int intermission)
 {
        prvm_prog_t *prog = CLVM_prog;
-       if(cl.csqc_loaded)
-       {
-               CSQC_BEGIN
+
+       if(prog->loaded)
                PRVM_clientglobalfloat(intermission) = intermission;
-               CSQC_END
-       }
 }
 void CL_VM_UpdateShowingScoresState (int showingscores)
 {
        prvm_prog_t *prog = CLVM_prog;
-       if(cl.csqc_loaded)
-       {
-               CSQC_BEGIN
+
+       if(prog->loaded)
                PRVM_clientglobalfloat(sb_showscores) = showingscores;
-               CSQC_END
-       }
 }
 qbool CL_VM_Event_Sound(int sound_num, float fvolume, int channel, float attenuation, int ent, vec3_t pos, int flags, float speed)
 {
        prvm_prog_t *prog = CLVM_prog;
        qbool r = false;
-       if(cl.csqc_loaded)
+
+       if(prog->loaded)
        {
-               CSQC_BEGIN
                if(PRVM_clientfunction(CSQC_Event_Sound))
                {
                        PRVM_clientglobalfloat(time) = cl.time;
                        PRVM_clientglobaledict(self) = cl.csqc_server2csqcentitynumber[cl.playerentity];
                        PRVM_G_FLOAT(OFS_PARM0) = ent;
                        PRVM_G_FLOAT(OFS_PARM1) = CHAN_ENGINE2USER(channel);
-                       PRVM_G_INT(OFS_PARM2) = PRVM_SetTempString(prog, cl.sound_name[sound_num] );
+                       PRVM_G_INT(OFS_PARM2) = PRVM_SetTempString(prog, cl.sound_name[sound_num], strlen(cl.sound_name[sound_num]));
                        PRVM_G_FLOAT(OFS_PARM3) = fvolume;
                        PRVM_G_FLOAT(OFS_PARM4) = attenuation;
                        VectorCopy(pos, PRVM_G_VECTOR(OFS_PARM5) );
@@ -741,7 +732,6 @@ qbool CL_VM_Event_Sound(int sound_num, float fvolume, int channel, float attenua
                        prog->ExecuteProgram(prog, PRVM_clientfunction(CSQC_Event_Sound), "QC function CSQC_Event_Sound is missing");
                        r = CSQC_RETURNVAL != 0;
                }
-               CSQC_END
        }
 
        return r;
@@ -753,15 +743,14 @@ static void CL_VM_UpdateCoopDeathmatchGlobals (int gametype)
        int localcoop;
        int localdeathmatch;
 
-       if(cl.csqc_loaded)
+       if(prog->loaded)
        {
                if(gametype == GAME_COOP)
                {
                        localcoop = 1;
                        localdeathmatch = 0;
                }
-               else
-               if(gametype == GAME_DEATHMATCH)
+               else if(gametype == GAME_DEATHMATCH)
                {
                        localcoop = 0;
                        localdeathmatch = 1;
@@ -773,10 +762,8 @@ static void CL_VM_UpdateCoopDeathmatchGlobals (int gametype)
                        localcoop = 0;
                        localdeathmatch = 0;
                }
-               CSQC_BEGIN
                PRVM_clientglobalfloat(coop) = localcoop;
                PRVM_clientglobalfloat(deathmatch) = localdeathmatch;
-               CSQC_END
        }
 }
 #if 0
@@ -784,9 +771,10 @@ static float CL_VM_Event (float event)             //[515]: needed ? I'd say "YES", but don
 {
        prvm_prog_t *prog = CLVM_prog;
        float r = 0;
-       if(!cl.csqc_loaded)
+
+       if(!prog->loaded)
                return 0;
-       CSQC_BEGIN
+
        if(PRVM_clientfunction(CSQC_Event))
        {
                PRVM_clientglobalfloat(time) = cl.time;
@@ -795,7 +783,6 @@ static float CL_VM_Event (float event)              //[515]: needed ? I'd say "YES", but don
                prog->ExecuteProgram(prog, PRVM_clientfunction(CSQC_Event), "QC function CSQC_Event is missing");
                r = CSQC_RETURNVAL;
        }
-       CSQC_END
        return r;
 }
 #endif
@@ -804,70 +791,69 @@ void CSQC_ReadEntities (void)
 {
        prvm_prog_t *prog = CLVM_prog;
        unsigned short entnum, oldself, realentnum;
-       if(!cl.csqc_loaded)
+
+       if(!prog->loaded)
        {
                Host_Error ("CSQC_ReadEntities: CSQC is not loaded");
                return;
        }
 
-       CSQC_BEGIN
-               PRVM_clientglobalfloat(time) = cl.time;
-               oldself = PRVM_clientglobaledict(self);
-               while(1)
+       PRVM_clientglobalfloat(time) = cl.time;
+       oldself = PRVM_clientglobaledict(self);
+       while(1)
+       {
+               entnum = MSG_ReadShort(&cl_message);
+               if(!entnum || cl_message.badread)
+                       break;
+               realentnum = entnum & 0x7FFF;
+               PRVM_clientglobaledict(self) = cl.csqc_server2csqcentitynumber[realentnum];
+               if(entnum & 0x8000)
                {
-                       entnum = MSG_ReadShort(&cl_message);
-                       if(!entnum || cl_message.badread)
-                               break;
-                       realentnum = entnum & 0x7FFF;
-                       PRVM_clientglobaledict(self) = cl.csqc_server2csqcentitynumber[realentnum];
-                       if(entnum & 0x8000)
+                       if(PRVM_clientglobaledict(self))
                        {
-                               if(PRVM_clientglobaledict(self))
-                               {
-                                       prog->ExecuteProgram(prog, PRVM_clientfunction(CSQC_Ent_Remove), "QC function CSQC_Ent_Remove is missing");
-                                       cl.csqc_server2csqcentitynumber[realentnum] = 0;
-                               }
-                               else
-                               {
-                                       // LadyHavoc: removing an entity that is already gone on
-                                       // the csqc side is possible for legitimate reasons (such
-                                       // as a repeat of the remove message), so no warning is
-                                       // needed
-                                       //Con_Printf("Bad csqc_server2csqcentitynumber map\n"); //[515]: never happens ?
-                               }
+                               prog->ExecuteProgram(prog, PRVM_clientfunction(CSQC_Ent_Remove), "QC function CSQC_Ent_Remove is missing");
+                               cl.csqc_server2csqcentitynumber[realentnum] = 0;
                        }
                        else
                        {
-                               if(!PRVM_clientglobaledict(self))
+                               // LadyHavoc: removing an entity that is already gone on
+                               // the csqc side is possible for legitimate reasons (such
+                               // as a repeat of the remove message), so no warning is
+                               // needed
+                               //Con_Printf("Bad csqc_server2csqcentitynumber map\n"); //[515]: never happens ?
+                       }
+               }
+               else
+               {
+                       if(!PRVM_clientglobaledict(self))
+                       {
+                               if(!PRVM_clientfunction(CSQC_Ent_Spawn))
                                {
-                                       if(!PRVM_clientfunction(CSQC_Ent_Spawn))
-                                       {
-                                               prvm_edict_t    *ed;
-                                               ed = PRVM_ED_Alloc(prog);
-                                               PRVM_clientedictfloat(ed, entnum) = realentnum;
-                                               PRVM_clientglobaledict(self) = cl.csqc_server2csqcentitynumber[realentnum] = PRVM_EDICT_TO_PROG(ed);
-                                       }
-                                       else
-                                       {
-                                               // entity( float entnum ) CSQC_Ent_Spawn;
-                                               // the qc function should set entnum, too (this way it also can return world [2/1/2008 Andreas]
-                                               PRVM_G_FLOAT(OFS_PARM0) = (float) realentnum;
-                                               // make sure no one gets wrong ideas
-                                               PRVM_clientglobaledict(self) = 0;
-                                               prog->ExecuteProgram(prog, PRVM_clientfunction(CSQC_Ent_Spawn), "QC function CSQC_Ent_Spawn is missing");
-                                               PRVM_clientglobaledict(self) = cl.csqc_server2csqcentitynumber[realentnum] = PRVM_EDICT( PRVM_G_INT( OFS_RETURN ) );
-                                       }
-                                       PRVM_G_FLOAT(OFS_PARM0) = 1;
-                                       prog->ExecuteProgram(prog, PRVM_clientfunction(CSQC_Ent_Update), "QC function CSQC_Ent_Update is missing");
+                                       prvm_edict_t    *ed;
+                                       ed = PRVM_ED_Alloc(prog);
+                                       PRVM_clientedictfloat(ed, entnum) = realentnum;
+                                       PRVM_clientglobaledict(self) = cl.csqc_server2csqcentitynumber[realentnum] = PRVM_EDICT_TO_PROG(ed);
                                }
-                               else {
-                                       PRVM_G_FLOAT(OFS_PARM0) = 0;
-                                       prog->ExecuteProgram(prog, PRVM_clientfunction(CSQC_Ent_Update), "QC function CSQC_Ent_Update is missing");
+                               else
+                               {
+                                       // entity( float entnum ) CSQC_Ent_Spawn;
+                                       // the qc function should set entnum, too (this way it also can return world [2/1/2008 Andreas]
+                                       PRVM_G_FLOAT(OFS_PARM0) = (float) realentnum;
+                                       // make sure no one gets wrong ideas
+                                       PRVM_clientglobaledict(self) = 0;
+                                       prog->ExecuteProgram(prog, PRVM_clientfunction(CSQC_Ent_Spawn), "QC function CSQC_Ent_Spawn is missing");
+                                       PRVM_clientglobaledict(self) = cl.csqc_server2csqcentitynumber[realentnum] = PRVM_EDICT( PRVM_G_INT( OFS_RETURN ) );
                                }
+                               PRVM_G_FLOAT(OFS_PARM0) = 1;
+                               prog->ExecuteProgram(prog, PRVM_clientfunction(CSQC_Ent_Update), "QC function CSQC_Ent_Update is missing");
+                       }
+                       else {
+                               PRVM_G_FLOAT(OFS_PARM0) = 0;
+                               prog->ExecuteProgram(prog, PRVM_clientfunction(CSQC_Ent_Update), "QC function CSQC_Ent_Update is missing");
                        }
                }
-               PRVM_clientglobaledict(self) = oldself;
-       CSQC_END
+       }
+       PRVM_clientglobaledict(self) = oldself;
 }
 
 static void CLVM_begin_increase_edicts(prvm_prog_t *prog)
@@ -883,7 +869,7 @@ static void CLVM_end_increase_edicts(prvm_prog_t *prog)
 
        // link every entity except world
        for (i = 1, ent = prog->edicts;i < prog->num_edicts;i++, ent++)
-               if (!ent->free && !VectorCompare(PRVM_clientedictvector(ent, absmin), PRVM_clientedictvector(ent, absmax)))
+               if (!ent->free)
                        CL_LinkEdict(ent);
 }
 
@@ -1083,7 +1069,7 @@ void CL_VM_Init (void)
        prog->error_cmd             = Host_Error;
        prog->ExecuteProgram        = CLVM_ExecuteProgram;
 
-       PRVM_Prog_Load(prog, csprogsfn, csprogsdata, csprogsdatasize, cl_numrequiredfunc, cl_required_func, CL_REQFIELDS, cl_reqfields, CL_REQGLOBALS, cl_reqglobals);
+       PRVM_Prog_Load(prog, csprogsfn, csprogsdata, csprogsdatasize, CL_CheckRequiredFuncs, CL_REQFIELDS, cl_reqfields, CL_REQGLOBALS, cl_reqglobals);
 
        if (!prog->loaded)
        {
@@ -1123,7 +1109,7 @@ void CL_VM_Init (void)
        PRVM_clientglobalfloat(time) = cl.time;
        PRVM_clientglobaledict(self) = 0;
 
-       PRVM_clientglobalstring(mapname) = PRVM_SetEngineString(prog, cl.worldname);
+       PRVM_clientglobalstring(mapname) = PRVM_SetEngineString(prog, cl.worldbasename);
        PRVM_clientglobalfloat(player_localnum) = cl.realplayerentity - 1;
        PRVM_clientglobalfloat(player_localentnum) = cl.viewentity;
 
@@ -1133,15 +1119,22 @@ void CL_VM_Init (void)
        VectorCopy(cl.world.maxs, PRVM_clientedictvector(prog->edicts, maxs));
        VectorCopy(cl.world.mins, PRVM_clientedictvector(prog->edicts, absmin));
        VectorCopy(cl.world.maxs, PRVM_clientedictvector(prog->edicts, absmax));
+       PRVM_clientedictfloat(prog->edicts, solid) = SOLID_BSP;
+       PRVM_clientedictfloat(prog->edicts, modelindex) = 1;
+       PRVM_clientedictstring(prog->edicts, model) = PRVM_SetEngineString(prog, cl.worldmodel->name);
 
-       // call the prog init
-       prog->ExecuteProgram(prog, PRVM_clientfunction(CSQC_Init), "QC function CSQC_Init is missing");
+       // call the prog init if it exists
+       if (PRVM_clientfunction(CSQC_Init))
+       {
+               PRVM_G_FLOAT(OFS_PARM0) = 1.0f; // CSQC_SIMPLE engines always pass 0, FTE always passes 1
+               PRVM_G_INT(OFS_PARM1) = PRVM_SetEngineString(prog, gamename);
+               PRVM_G_FLOAT(OFS_PARM2) = 1.0f; // TODO DP versions...
+               prog->ExecuteProgram(prog, PRVM_clientfunction(CSQC_Init), "QC function CSQC_Init is missing");
+       }
 
        // Once CSQC_Init was called, we consider csqc code fully initialized.
        prog->inittime = host.realtime;
 
-       cl.csqc_loaded = true;
-
        cl.csqc_vidvars.drawcrosshair = false;
        cl.csqc_vidvars.drawenginesbar = false;
 
@@ -1153,11 +1146,9 @@ void CL_VM_ShutDown (void)
 {
        prvm_prog_t *prog = CLVM_prog;
        Cmd_ClearCSQCCommands(cmd_local);
+
        //Cvar_SetValueQuick(&csqc_progcrc, -1);
        //Cvar_SetValueQuick(&csqc_progsize, -1);
-       if(!cl.csqc_loaded)
-               return;
-CSQC_BEGIN
        if (prog->loaded)
        {
                PRVM_clientglobalfloat(time) = cl.time;
@@ -1166,9 +1157,7 @@ CSQC_BEGIN
                        prog->ExecuteProgram(prog, PRVM_clientfunction(CSQC_Shutdown), "QC function CSQC_Shutdown is missing");
        }
        PRVM_Prog_Reset(prog);
-CSQC_END
        Con_DPrint("CSQC ^1unloaded\n");
-       cl.csqc_loaded = false;
 }
 
 qbool CL_VM_GetEntitySoundOrigin(int entnum, vec3_t out)
@@ -1179,8 +1168,6 @@ qbool CL_VM_GetEntitySoundOrigin(int entnum, vec3_t out)
        matrix4x4_t matrix;
        qbool r = 0;
 
-       CSQC_BEGIN;
-
        ed = PRVM_EDICT_NUM(entnum - MAX_EDICTS);
 
        if(!ed->free)
@@ -1194,8 +1181,6 @@ qbool CL_VM_GetEntitySoundOrigin(int entnum, vec3_t out)
                r = 1;
        }
 
-       CSQC_END;
-
        return r;
 }
 
@@ -1207,38 +1192,36 @@ qbool CL_VM_TransformView(int entnum, matrix4x4_t *viewmatrix, mplane_t *clippla
        vec3_t forward, left, up, origin, ang;
        matrix4x4_t mat, matq;
 
-       CSQC_BEGIN
-               ed = PRVM_EDICT_NUM(entnum);
-               // camera:
-               //   camera_transform
-               if(PRVM_clientedictfunction(ed, camera_transform))
+       ed = PRVM_EDICT_NUM(entnum);
+       // camera:
+       //   camera_transform
+       if(PRVM_clientedictfunction(ed, camera_transform))
+       {
+               ret = true;
+               if(viewmatrix && clipplane && visorigin)
                {
-                       ret = true;
-                       if(viewmatrix && clipplane && visorigin)
-                       {
-                               Matrix4x4_ToVectors(viewmatrix, forward, left, up, origin);
-                               AnglesFromVectors(ang, forward, up, false);
-                               PRVM_clientglobalfloat(time) = cl.time;
-                               PRVM_clientglobaledict(self) = entnum;
-                               VectorCopy(origin, PRVM_G_VECTOR(OFS_PARM0));
-                               VectorCopy(ang, PRVM_G_VECTOR(OFS_PARM1));
-                               VectorCopy(forward, PRVM_clientglobalvector(v_forward));
-                               VectorScale(left, -1, PRVM_clientglobalvector(v_right));
-                               VectorCopy(up, PRVM_clientglobalvector(v_up));
-                               VectorCopy(origin, PRVM_clientglobalvector(trace_endpos));
-                               prog->ExecuteProgram(prog, PRVM_clientedictfunction(ed, camera_transform), "QC function e.camera_transform is missing");
-                               VectorCopy(PRVM_G_VECTOR(OFS_RETURN), origin);
-                               VectorCopy(PRVM_clientglobalvector(v_forward), forward);
-                               VectorScale(PRVM_clientglobalvector(v_right), -1, left);
-                               VectorCopy(PRVM_clientglobalvector(v_up), up);
-                               VectorCopy(PRVM_clientglobalvector(trace_endpos), visorigin);
-                               Matrix4x4_Invert_Full(&mat, viewmatrix);
-                               Matrix4x4_FromVectors(viewmatrix, forward, left, up, origin);
-                               Matrix4x4_Concat(&matq, viewmatrix, &mat);
-                               Matrix4x4_TransformPositivePlane(&matq, clipplane->normal[0], clipplane->normal[1], clipplane->normal[2], clipplane->dist, clipplane->normal_and_dist);
-                       }
+                       Matrix4x4_ToVectors(viewmatrix, forward, left, up, origin);
+                       AnglesFromVectors(ang, forward, up, false);
+                       PRVM_clientglobalfloat(time) = cl.time;
+                       PRVM_clientglobaledict(self) = entnum;
+                       VectorCopy(origin, PRVM_G_VECTOR(OFS_PARM0));
+                       VectorCopy(ang, PRVM_G_VECTOR(OFS_PARM1));
+                       VectorCopy(forward, PRVM_clientglobalvector(v_forward));
+                       VectorScale(left, -1, PRVM_clientglobalvector(v_right));
+                       VectorCopy(up, PRVM_clientglobalvector(v_up));
+                       VectorCopy(origin, PRVM_clientglobalvector(trace_endpos));
+                       prog->ExecuteProgram(prog, PRVM_clientedictfunction(ed, camera_transform), "QC function e.camera_transform is missing");
+                       VectorCopy(PRVM_G_VECTOR(OFS_RETURN), origin);
+                       VectorCopy(PRVM_clientglobalvector(v_forward), forward);
+                       VectorScale(PRVM_clientglobalvector(v_right), -1, left);
+                       VectorCopy(PRVM_clientglobalvector(v_up), up);
+                       VectorCopy(PRVM_clientglobalvector(trace_endpos), visorigin);
+                       Matrix4x4_Invert_Full(&mat, viewmatrix);
+                       Matrix4x4_FromVectors(viewmatrix, forward, left, up, origin);
+                       Matrix4x4_Concat(&matq, viewmatrix, &mat);
+                       Matrix4x4_TransformPositivePlane(&matq, clipplane->normal[0], clipplane->normal[1], clipplane->normal[2], clipplane->dist, clipplane->normal_and_dist);
                }
-       CSQC_END
+       }
 
        return ret;
 }
index 4760a7f3c68b5caae93f5840e2268cb4a81eb3c6..1900f91c3711c82805bf65fcabab4cc8cef4c5bc 100644 (file)
--- a/csprogs.h
+++ b/csprogs.h
@@ -111,20 +111,20 @@ void CL_VM_ShutDown(void);
 void CL_VM_UpdateIntermissionState(int intermission);
 void CL_VM_UpdateShowingScoresState(int showingscores);
 qbool CL_VM_InputEvent(int eventtype, float x, float y);
-qbool CL_VM_ConsoleCommand(const char *text);
+qbool CL_VM_ConsoleCommand(const char *text, size_t textlen);
 void CL_VM_UpdateDmgGlobals(int dmg_take, int dmg_save, vec3_t dmg_origin);
 void CL_VM_UpdateIntermissionState(int intermission);
 qbool CL_VM_Event_Sound(int sound_num, float volume, int channel, float attenuation, int ent, vec3_t pos, int flags, float speed);
 qbool CL_VM_Parse_TempEntity(void);
-void CL_VM_Parse_StuffCmd(const char *msg);
-void CL_VM_Parse_CenterPrint(const char *msg);
+void CL_VM_Parse_StuffCmd(const char *msg, size_t msg_len);
+void CL_VM_Parse_CenterPrint(const char *msg, size_t msg_len);
 int CL_GetPitchSign(prvm_prog_t *prog, prvm_edict_t *ent);
 int CL_GetTagMatrix(prvm_prog_t *prog, matrix4x4_t *out, prvm_edict_t *ent, int tagindex, prvm_vec_t *shadingorigin);
 void CL_GetEntityMatrix(prvm_prog_t *prog, prvm_edict_t *ent, matrix4x4_t *out, qbool viewmatrix);
 void QW_CL_StartUpload(unsigned char *data, int size);
 
 void CSQC_UpdateNetworkTimes(double newtime, double oldtime);
-void CSQC_AddPrintText(const char *msg);
+void CSQC_AddPrintText(const char *msg, size_t msg_len);
 void CSQC_ReadEntities(void);
 void CSQC_RelinkAllEntities(int drawmask);
 void CSQC_RelinkCSQCEntities(void);
diff --git a/cvar.c b/cvar.c
index 7936c92356be359c13748eca7afbec3250df8ac2..1c347193787b09119f06f782ce3d880b53c78d3d 100644 (file)
--- a/cvar.c
+++ b/cvar.c
@@ -33,9 +33,9 @@ cvar_state_t cvars_null;
 Cvar_FindVar
 ============
 */
-cvar_t *Cvar_FindVar(cvar_state_t *cvars, const char *var_name, int neededflags)
+cvar_t *Cvar_FindVar(cvar_state_t *cvars, const char *var_name, unsigned neededflags)
 {
-       int hashindex;
+       unsigned hashindex;
        cvar_hash_t *hash;
 
        // use hash lookup to minimize search time
@@ -50,7 +50,7 @@ cvar_t *Cvar_FindVar(cvar_state_t *cvars, const char *var_name, int neededflags)
        return NULL;
 }
 
-cvar_t *Cvar_FindVarAfter(cvar_state_t *cvars, const char *prev_var_name, int neededflags)
+cvar_t *Cvar_FindVarAfter(cvar_state_t *cvars, const char *prev_var_name, unsigned neededflags)
 {
        cvar_t *var;
 
@@ -74,37 +74,41 @@ cvar_t *Cvar_FindVarAfter(cvar_state_t *cvars, const char *prev_var_name, int ne
        return var;
 }
 
-static cvar_t *Cvar_FindVarLink(cvar_state_t *cvars, const char *var_name, cvar_t **parent, cvar_t ***link, cvar_t **prev_alpha, int neededflags)
+/**
+ * Returns a pointer to the pointer stored in hashtable[] (or the one it links to)
+ * because we'll need to update that when deleting a cvar as other cvar(s) may share its hashindex.
+ */
+static cvar_hash_t **Cvar_FindVarLink(cvar_state_t *cvars, const char *var_name, cvar_t **prev_alpha, unsigned neededflags)
 {
-       int hashindex;
-       cvar_hash_t *hash;
+       unsigned hashindex;
+       cvar_t *cvar;
+       cvar_hash_t **hashlinkptr;
 
        // use hash lookup to minimize search time
        hashindex = CRC_Block((const unsigned char *)var_name, strlen(var_name)) % CVAR_HASHSIZE;
-       if(parent) *parent = NULL;
        if(prev_alpha) *prev_alpha = NULL;
-       if(link) *link = &cvars->hashtable[hashindex]->cvar;
-       for (hash = cvars->hashtable[hashindex];hash;hash = hash->next)
+       hashlinkptr = &cvars->hashtable[hashindex];
+       for (hashlinkptr = &cvars->hashtable[hashindex]; *hashlinkptr; hashlinkptr = &(*hashlinkptr)->next)
        {
-               if (!strcmp (var_name, hash->cvar->name) && (hash->cvar->flags & neededflags))
+               cvar = (*hashlinkptr)->cvar;
+               if (!strcmp (var_name, cvar->name) && (cvar->flags & neededflags))
                        goto match;
                else
-                       for (char **alias = hash->cvar->aliases; alias && *alias; alias++)
-                               if (!strcmp (var_name, *alias) && (hash->cvar->flags & neededflags))
+                       for (char **alias = cvar->aliases; alias && *alias; alias++)
+                               if (!strcmp (var_name, *alias) && (cvar->flags & neededflags))
                                        goto match;
-               if(parent) *parent = hash->cvar;
        }
        return NULL;
-match:
-       if(!prev_alpha || hash->cvar == cvars->vars)
-               return hash->cvar;
 
+match:
+       if(!prev_alpha || cvar == cvars->vars)
+               return hashlinkptr;
        *prev_alpha = cvars->vars;
        // if prev_alpha happens to become NULL then there has been some inconsistency elsewhere
        // already - should I still insert '*prev_alpha &&' in the loop?
-       while((*prev_alpha)->next != hash->cvar)
+       while((*prev_alpha)->next != cvar)
                *prev_alpha = (*prev_alpha)->next;
-       return hash->cvar;
+       return hashlinkptr;
 }
 
 /*
@@ -112,7 +116,7 @@ match:
 Cvar_VariableValue
 ============
 */
-float Cvar_VariableValueOr(cvar_state_t *cvars, const char *var_name, float def, int neededflags)
+float Cvar_VariableValueOr(cvar_state_t *cvars, const char *var_name, float def, unsigned neededflags)
 {
        cvar_t *var;
 
@@ -122,7 +126,7 @@ float Cvar_VariableValueOr(cvar_state_t *cvars, const char *var_name, float def,
        return atof (var->string);
 }
 
-float Cvar_VariableValue(cvar_state_t *cvars, const char *var_name, int neededflags)
+float Cvar_VariableValue(cvar_state_t *cvars, const char *var_name, unsigned neededflags)
 {
        return Cvar_VariableValueOr(cvars, var_name, 0, neededflags);
 }
@@ -132,7 +136,7 @@ float Cvar_VariableValue(cvar_state_t *cvars, const char *var_name, int neededfl
 Cvar_VariableString
 ============
 */
-const char *Cvar_VariableStringOr(cvar_state_t *cvars, const char *var_name, const char *def, int neededflags)
+const char *Cvar_VariableStringOr(cvar_state_t *cvars, const char *var_name, const char *def, unsigned neededflags)
 {
        cvar_t *var;
 
@@ -142,7 +146,7 @@ const char *Cvar_VariableStringOr(cvar_state_t *cvars, const char *var_name, con
        return var->string;
 }
 
-const char *Cvar_VariableString(cvar_state_t *cvars, const char *var_name, int neededflags)
+const char *Cvar_VariableString(cvar_state_t *cvars, const char *var_name, unsigned neededflags)
 {
        return Cvar_VariableStringOr(cvars, var_name, cvar_null_string, neededflags);
 }
@@ -152,7 +156,7 @@ const char *Cvar_VariableString(cvar_state_t *cvars, const char *var_name, int n
 Cvar_VariableDefString
 ============
 */
-const char *Cvar_VariableDefString(cvar_state_t *cvars, const char *var_name, int neededflags)
+const char *Cvar_VariableDefString(cvar_state_t *cvars, const char *var_name, unsigned neededflags)
 {
        cvar_t *var;
 
@@ -167,7 +171,7 @@ const char *Cvar_VariableDefString(cvar_state_t *cvars, const char *var_name, in
 Cvar_VariableDescription
 ============
 */
-const char *Cvar_VariableDescription(cvar_state_t *cvars, const char *var_name, int neededflags)
+const char *Cvar_VariableDescription(cvar_state_t *cvars, const char *var_name, unsigned neededflags)
 {
        cvar_t *var;
 
@@ -183,7 +187,7 @@ const char *Cvar_VariableDescription(cvar_state_t *cvars, const char *var_name,
 Cvar_CompleteVariable
 ============
 */
-const char *Cvar_CompleteVariable(cvar_state_t *cvars, const char *partial, int neededflags)
+const char *Cvar_CompleteVariable(cvar_state_t *cvars, const char *partial, unsigned neededflags)
 {
        cvar_t          *cvar;
        size_t          len;
@@ -210,7 +214,7 @@ const char *Cvar_CompleteVariable(cvar_state_t *cvars, const char *partial, int
        Thanks to Fett erich@heintz.com
 
 */
-int Cvar_CompleteCountPossible(cvar_state_t *cvars, const char *partial, int neededflags)
+int Cvar_CompleteCountPossible(cvar_state_t *cvars, const char *partial, unsigned neededflags)
 {
        cvar_t  *cvar;
        size_t  len;
@@ -243,7 +247,7 @@ int Cvar_CompleteCountPossible(cvar_state_t *cvars, const char *partial, int nee
        Thanks to taniwha
 
 */
-const char **Cvar_CompleteBuildList(cvar_state_t *cvars, const char *partial, int neededflags)
+const char **Cvar_CompleteBuildList(cvar_state_t *cvars, const char *partial, unsigned neededflags)
 {
        const cvar_t *cvar;
        size_t len = 0;
@@ -271,19 +275,17 @@ void Cvar_PrintHelp(cvar_t *cvar, const char *name, qbool full)
 {
        // Aliases are purple, cvars are yellow
        if (strcmp(cvar->name, name))
-               Con_Printf("^6");
+               Con_Printf("^6%s^7 (alias of ^3%s^7)", name, cvar->name);
        else
-               Con_Printf("^3");
-       Con_Printf("%s^7 is \"%s\" [\"%s\"]", name, ((cvar->flags & CF_PRIVATE) ? "********"/*hunter2*/ : cvar->string), cvar->defstring);
-       if (strcmp(cvar->name, name))
-               Con_Printf(" (also ^3%s^7)", cvar->name);
+               Con_Printf("^3%s^7", name);
+       Con_Printf(" is \"%s^7\" [\"%s^7\"]", ((cvar->flags & CF_PRIVATE) ? "********"/*hunter2*/ : cvar->string), cvar->defstring);
        if (full)
                Con_Printf(" %s", cvar->description);
-       Con_Printf("\n");
+       Con_Print("\n");
 }
 
 // written by LadyHavoc
-void Cvar_CompleteCvarPrint(cvar_state_t *cvars, const char *partial, int neededflags)
+void Cvar_CompleteCvarPrint(cvar_state_t *cvars, const char *partial, unsigned neededflags)
 {
        cvar_t *cvar;
        size_t len = strlen(partial);
@@ -299,8 +301,9 @@ void Cvar_CompleteCvarPrint(cvar_state_t *cvars, const char *partial, int needed
                
 }
 
-// check if a cvar is held by some progs
-static qbool Cvar_IsAutoCvar(cvar_t *var)
+/// Check if a cvar is held by some progs
+/// in which case its name is returned, otherwise NULL.
+static const char *Cvar_IsAutoCvar(cvar_t *var)
 {
        int i;
        prvm_prog_t *prog;
@@ -308,12 +311,12 @@ static qbool Cvar_IsAutoCvar(cvar_t *var)
        {
                prog = &prvm_prog_list[i];
                if (prog->loaded && var->globaldefindex[i] >= 0)
-                       return true;
+                       return prog->name;
        }
-       return false;
+       return NULL;
 }
 
-// we assume that prog is already set to the target progs
+/// we assume that prog is already set to the target progs
 static void Cvar_UpdateAutoCvar(cvar_t *var)
 {
        int i;
@@ -326,7 +329,7 @@ static void Cvar_UpdateAutoCvar(cvar_t *var)
                prog = &prvm_prog_list[i];
                if (prog->loaded && var->globaldefindex[i] >= 0)
                {
-                       // MUST BE SYNCED WITH prvm_edict.c PRVM_LoadProgs
+                       // MUST BE SYNCED WITH prvm_edict.c PRVM_Prog_Load
                        switch(prog->globaldefs[var->globaldefindex[i]].type & ~DEF_SAVEGLOBAL)
                        {
                        case ev_float:
@@ -358,7 +361,7 @@ static void Cvar_UpdateAutoCvar(cvar_t *var)
        }
 }
 
-// called after loading a savegame
+/// called after loading a savegame
 void Cvar_UpdateAllAutoCvars(cvar_state_t *cvars)
 {
        cvar_t *var;
@@ -370,7 +373,7 @@ void Cvar_Callback(cvar_t *var)
 {
        if (var == NULL)
        {
-               Con_Print("Cvar_Callback: var == NULL\n");
+               Con_Print(CON_WARN "Cvar_Callback: var == NULL\n");
                return;
        }
 
@@ -392,7 +395,7 @@ static void Cvar_SetQuick_Internal (cvar_t *var, const char *value)
        changed = strcmp(var->string, value) != 0;
        // LadyHavoc: don't reallocate when there is no change
        if (!changed)
-               return;
+               goto cvar_callback;
 
        // LadyHavoc: don't reallocate when the buffer is the same size
        valuelen = strlen(value);
@@ -425,6 +428,7 @@ static void Cvar_SetQuick_Internal (cvar_t *var, const char *value)
 
        Cvar_UpdateAutoCvar(var);
 
+cvar_callback:
        // Call the function stored in the cvar for bounds checking, cleanup, etc
        Cvar_Callback(var);
 }
@@ -433,10 +437,16 @@ void Cvar_SetQuick (cvar_t *var, const char *value)
 {
        if (var == NULL)
        {
-               Con_Print("Cvar_SetQuick: var == NULL\n");
+               Con_Print(CON_WARN "Cvar_SetQuick: var == NULL\n");
                return;
        }
 
+       if (!(var->flags & CF_REGISTERED) && !(var->flags & CF_ALLOCATED))
+       {
+               Con_Printf(CON_WARN "Warning: Cvar_SetQuick() cannot set unregistered cvar \"%s\"\n", var->name);
+               return; // setting an unregistered engine cvar crashes
+       }
+
        if (developer_extra.integer)
                Con_DPrintf("Cvar_SetQuick({\"%s\", \"%s\", %i, \"%s\"}, \"%s\");\n", var->name, var->string, var->flags, var->defstring, value);
 
@@ -449,7 +459,7 @@ void Cvar_Set(cvar_state_t *cvars, const char *var_name, const char *value)
        var = Cvar_FindVar(cvars, var_name, ~0);
        if (var == NULL)
        {
-               Con_Printf("Cvar_Set: variable %s not found\n", var_name);
+               Con_Printf(CON_WARN "Cvar_Set: variable %s not found\n", var_name);
                return;
        }
        Cvar_SetQuick(var, value);
@@ -486,9 +496,17 @@ void Cvar_RegisterCallback(cvar_t *variable, void (*callback)(cvar_t *))
 {
        if (variable == NULL)
        {
-               Con_Print("Cvar_RegisterCallback: var == NULL\n");
+               Con_Print(CON_WARN "Cvar_RegisterCallback: var == NULL\n");
                return;
        }
+
+       if (!(variable->flags & cmd_local->cvars_flagsmask))
+       {
+               if (developer_extra.integer)
+                       Con_DPrintf("^6Cvar_RegisterCallback: rejecting cvar \"%s\"\n", variable->name);
+               return;
+       }
+
        variable->callback = callback;
 }
 
@@ -496,24 +514,31 @@ void Cvar_RegisterVirtual(cvar_t *variable, const char *name )
 {
        cvar_state_t *cvars = &cvars_all;
        cvar_hash_t *hash;
-       int hashindex;
+       unsigned hashindex;
+
+       if (!(variable->flags & cmd_local->cvars_flagsmask))
+       {
+               if (developer_extra.integer)
+                       Con_DPrintf("^6Cvar_RegisterVirtual: rejecting cvar \"%s\" alias \"%s\"\n", variable->name, name);
+               return;
+       }
 
        if(!*name)
        {
-               Con_Printf("Cvar_RegisterVirtual: invalid virtual cvar name\n");
+               Con_Printf(CON_WARN "Cvar_RegisterVirtual: invalid virtual cvar name\n");
                return;
        }
 
        // check for overlap with a command
        if (Cmd_Exists(cmd_local, name))
        {
-               Con_Printf("Cvar_RegisterVirtual: %s is a command\n", name);
+               Con_Printf(CON_WARN "Cvar_RegisterVirtual: %s is a command\n", name);
                return;
        }
 
        if(Cvar_FindVar(&cvars_all, name, 0))
        {
-               Con_Printf("Cvar_RegisterVirtual: %s is a cvar\n", name);
+               Con_Printf(CON_WARN "Cvar_RegisterVirtual: %s is a cvar\n", name);
                return;
        }
 
@@ -522,7 +547,7 @@ void Cvar_RegisterVirtual(cvar_t *variable, const char *name )
        // Also if aliases is NULL this allocates fresh for the correct size, so it's fine to just do this.
        variable->aliases = (char **)Z_Realloc(variable->aliases, sizeof(char *) * (variable->aliases_size + 2));
        // Add the new alias, and increment the number of aliases in the list
-       variable->aliases[variable->aliases_size++] = (char *)Z_strdup(name);
+       variable->aliases[variable->aliases_size++] = Z_strdup(name);
 
        // link to head of list in this hash table index
        hash = (cvar_hash_t *)Z_Malloc(sizeof(cvar_hash_t));
@@ -543,7 +568,7 @@ static void Cvar_Link(cvar_t *variable, cvar_state_t *cvars)
 {
        cvar_t *current, *next;
        cvar_hash_t *hash;
-       int hashindex;
+       unsigned hashindex;
        /*
         * Link the variable in
         * alphanumerical order
@@ -573,23 +598,15 @@ Adds a freestanding variable to the variable list.
 */
 void Cvar_RegisterVariable (cvar_t *variable)
 {
-       cvar_state_t *cvars = NULL;
+       cvar_state_t *cvars = &cvars_all;
        cvar_t *current, *cvar;
        int i;
 
-       switch (variable->flags & (CF_CLIENT | CF_SERVER))
+       if (!(variable->flags & cmd_local->cvars_flagsmask))
        {
-       case CF_CLIENT:
-       case CF_SERVER:
-       case CF_CLIENT | CF_SERVER:
-               cvars = &cvars_all;
-               break;
-       case 0:
-               Sys_Error("Cvar_RegisterVariable({\"%s\", \"%s\", %i}) with no CF_CLIENT | CF_SERVER flags\n", variable->name, variable->string, variable->flags);
-               break;
-       default:
-               Sys_Error("Cvar_RegisterVariable({\"%s\", \"%s\", %i}) with weird CF_CLIENT | CF_SERVER flags\n", variable->name, variable->string, variable->flags);
-               break;
+               if (developer_extra.integer)
+                       Con_DPrintf("^2Cvar_RegisterVariable: rejecting cvar \"%s\"\n", variable->name);
+               return;
        }
 
        if (developer_extra.integer)
@@ -607,7 +624,7 @@ void Cvar_RegisterVariable (cvar_t *variable)
                        // (because the engine directly accesses fixed variables)
                        // NOTE: this isn't actually used currently
                        // (all cvars are registered before config parsing)
-                       variable->flags |= (cvar->flags & ~CF_ALLOCATED);
+                       variable->flags &= ~CF_ALLOCATED;
                        // cvar->string is now owned by variable instead
                        variable->string = cvar->string;
                        variable->defstring = cvar->defstring;
@@ -644,24 +661,27 @@ void Cvar_RegisterVariable (cvar_t *variable)
        // check for overlap with a command
        if (Cmd_Exists(cmd_local, variable->name))
        {
-               Con_Printf("Cvar_RegisterVariable: %s is a command\n", variable->name);
+               Con_Printf(CON_WARN "Cvar_RegisterVariable: %s is a command\n", variable->name);
                return;
        }
 
        // copy the value off, because future sets will Z_Free it
-       variable->name = (char *)Mem_strdup(zonemempool, variable->name);
-       variable->string = (char *)Mem_strdup(zonemempool, variable->string);
-       variable->defstring = (char *)Mem_strdup(zonemempool, variable->string);
+       variable->name = Z_strdup(variable->name);
+       variable->string = Z_strdup(variable->string);
+       variable->defstring = Z_strdup(variable->string);
        variable->value = atof (variable->string);
        variable->integer = (int) variable->value;
        variable->aliases = NULL;
        variable->aliases_size = 0;
-       variable->initstate = NULL;
+       variable->initstring = NULL;
 
        // Mark it as not an autocvar.
        for (i = 0;i < PRVM_PROG_MAX;i++)
                variable->globaldefindex[i] = -1;
 
+       // Safe for Cvar_SetQuick()
+       variable->flags |= CF_REGISTERED;
+
        Cvar_Link(variable, cvars);
 }
 
@@ -672,7 +692,7 @@ Cvar_Get
 Adds a newly allocated variable to the variable list or sets its value.
 ============
 */
-cvar_t *Cvar_Get(cvar_state_t *cvars, const char *name, const char *value, int flags, const char *newdescription)
+cvar_t *Cvar_Get(cvar_state_t *cvars, const char *name, const char *value, unsigned flags, const char *newdescription)
 {
        cvar_t *cvar;
        int i;
@@ -692,7 +712,7 @@ cvar_t *Cvar_Get(cvar_state_t *cvars, const char *name, const char *value, int f
                                Z_Free((char *)cvar->description);
 
                        if(*newdescription)
-                               cvar->description = (char *)Mem_strdup(zonemempool, newdescription);
+                               cvar->description = Z_strdup(newdescription);
                        else
                                cvar->description = cvar_dummy_description;
                }
@@ -702,14 +722,14 @@ cvar_t *Cvar_Get(cvar_state_t *cvars, const char *name, const char *value, int f
        // check for pure evil
        if (!*name)
        {
-               Con_Printf("Cvar_Get: invalid variable name\n");
+               Con_Printf(CON_WARN "Cvar_Get: invalid variable name\n");
                return NULL;
        }
 
        // check for overlap with a command
        if (Cmd_Exists(cmd_local, name))
        {
-               Con_Printf("Cvar_Get: %s is a command\n", name);
+               Con_Printf(CON_WARN "Cvar_Get: %s is a command\n", name);
                return NULL;
        }
 
@@ -718,17 +738,17 @@ cvar_t *Cvar_Get(cvar_state_t *cvars, const char *name, const char *value, int f
        // FIXME: these never get Z_Free'd
        cvar = (cvar_t *)Z_Malloc(sizeof(cvar_t));
        cvar->flags = flags | CF_ALLOCATED;
-       cvar->name = (char *)Mem_strdup(zonemempool, name);
-       cvar->string = (char *)Mem_strdup(zonemempool, value);
-       cvar->defstring = (char *)Mem_strdup(zonemempool, value);
+       cvar->name = Z_strdup(name);
+       cvar->string = Z_strdup(value);
+       cvar->defstring = Z_strdup(value);
        cvar->value = atof (cvar->string);
        cvar->integer = (int) cvar->value;
        cvar->aliases = NULL;
        cvar->aliases_size = 0;
-       cvar->initstate = NULL;
+       cvar->initstring = NULL;
 
        if(newdescription && *newdescription)
-               cvar->description = (char *)Mem_strdup(zonemempool, newdescription);
+               cvar->description = Z_strdup(newdescription);
        else
                cvar->description = cvar_dummy_description; // actually checked by VM_cvar_type
 
@@ -741,14 +761,63 @@ cvar_t *Cvar_Get(cvar_state_t *cvars, const char *name, const char *value, int f
        return cvar;
 }
 
+/// For "quiet" mode pass NULL as the callername.
+/// Returns true if the cvar was deleted.
+static qbool Cvar_Delete(cvar_state_t *cvars, const char *name, const char *callername)
+{
+       cvar_t *cvar, *prev;
+       cvar_hash_t **hashlinkptr, *oldhashlink;
+       const char *progname;
+
+       hashlinkptr = Cvar_FindVarLink(cvars, name, &prev, ~0);
+       if(!hashlinkptr)
+       {
+               if (callername)
+                       Con_Printf("%s: cvar \"%s\" is not defined.\n", callername, name);
+               return false;
+       }
+       cvar = (*hashlinkptr)->cvar;
+
+       if(!(cvar->flags & CF_ALLOCATED))
+       {
+               if (callername)
+                       Con_Printf(CON_WARN "%s: engine cvar \"%s\" cannot be deleted!\n", callername, cvar->name);
+               return false;
+       }
+       if ((progname = Cvar_IsAutoCvar(cvar)))
+       {
+               if (callername)
+                       Con_Printf(CON_WARN "%s: unable to delete cvar \"%s\", it is an autocvar used by running %s progs!\n", callername, cvar->name, progname);
+               return false;
+       }
+
+       if(cvar == cvars->vars)
+               cvars->vars = cvar->next;
+       else
+               prev->next = cvar->next;
+
+       if(cvar->description != cvar_dummy_description)
+               Z_Free((char *)cvar->description);
+       Z_Free((char *)cvar->name);
+       Z_Free((char *)cvar->string);
+       Z_Free((char *)cvar->defstring);
+       Z_Free(cvar);
+
+       oldhashlink = *hashlinkptr;
+       *hashlinkptr = (*hashlinkptr)->next;
+       Z_Free(oldhashlink);
+
+       return true;
+}
+
 qbool Cvar_Readonly (cvar_t *var, const char *cmd_name)
 {
        if (var->flags & CF_READONLY)
        {
+               Con_Print(CON_WARN);
                if(cmd_name)
                        Con_Printf("%s: ",cmd_name);
-               Con_Printf("%s", var->name);
-               Con_Printf(" is read-only\n");
+               Con_Printf("%s is read-only\n", var->name);
                return true;
        }
        return false;
@@ -823,86 +892,47 @@ void Cvar_LockDefaults_f(cmd_state_t *cmd)
 void Cvar_SaveInitState(cvar_state_t *cvars)
 {
        cvar_t *c;
+
        for (c = cvars->vars;c;c = c->next)
-       {
-               c->initstate = (cvar_t *)Z_Malloc(sizeof(cvar_t));
-               memcpy(c->initstate, c, sizeof(cvar_t));
-       }
+               if ((c->flags & (CF_PERSISTENT | CF_READONLY)) == 0)
+                       c->initstring = Z_strdup(c->string);
 }
 
 void Cvar_RestoreInitState(cvar_state_t *cvars)
 {
-       int hashindex;
-       cvar_t *c, **cp;
-       cvar_t *c2, **cp2;
-       for (cp = &cvars->vars;(c = *cp);)
+       cvar_t *c, *cp;
+
+       for (cp = cvars->vars; (c = cp);)
        {
-               if (c->initstate)
+               cp = c->next; // get next cvar now in case we delete this cvar
+
+               if (c->initstring)
                {
                        // restore this cvar, it existed at init
-                       if (((c->flags ^ c->initstate->flags) & CF_MAXFLAGSVAL)
-                        || strcmp(c->defstring ? c->defstring : "", c->initstate->defstring ? c->initstate->defstring : "")
-                        || strcmp(c->string ? c->string : "", c->initstate->string ? c->initstate->string : ""))
-                       {
-                               Con_DPrintf("Cvar_RestoreInitState: Restoring cvar \"%s\"\n", c->name);
-                               if (c->defstring)
-                                       Z_Free((char *)c->defstring);
-                               c->defstring = Mem_strdup(zonemempool, c->initstate->defstring);
-                               if (c->string)
-                                       Z_Free((char *)c->string);
-                               c->string = Mem_strdup(zonemempool, c->initstate->string);
-                       }
-                       c->flags = c->initstate->flags;
-                       c->value = c->initstate->value;
-                       c->integer = c->initstate->integer;
-                       VectorCopy(c->initstate->vector, c->vector);
-                       cp = &c->next;
+                       Con_DPrintf("Cvar_RestoreInitState: Restoring cvar \"%s\"\n", c->name);
+
+                       /* bones_was_here: intentionally NOT restoring defstring in this function.
+                        * The only callsite, Host_LoadConfig_f, will re-exec quake.rc, default.cfg, etc.
+                        * Defaults are unlocked here, so Cvar_LockDefaults_f will reset the defstring.
+                        * This is more correct than restoring the defstring here
+                        * because a gamedir change could load configs that should change defaults.
+                        */
+
+                       Cvar_SetQuick(c, c->initstring);
+                       c->flags &= ~CF_DEFAULTSET;
                }
-               else
+               else if (!(c->flags & (CF_PERSISTENT | CF_READONLY))) // cvars with those flags have no initstring AND may not be deleted or reset
                {
-                       if (!(c->flags & CF_ALLOCATED))
-                       {
-                               Con_DPrintf("Cvar_RestoreInitState: Unable to destroy cvar \"%s\", it was registered after init!\n", c->name);
-                               // In this case, at least reset it to the default.
-                               if((c->flags & CF_PERSISTENT) == 0)
-                                       Cvar_SetQuick(c, c->defstring);
-                               cp = &c->next;
-                               continue;
-                       }
-                       if (Cvar_IsAutoCvar(c))
-                       {
-                               Con_DPrintf("Cvar_RestoreInitState: Unable to destroy cvar \"%s\", it is an autocvar used by running progs!\n", c->name);
-                               // In this case, at least reset it to the default.
-                               if((c->flags & CF_PERSISTENT) == 0)
-                                       Cvar_SetQuick(c, c->defstring);
-                               cp = &c->next;
-                               continue;
-                       }
                        // remove this cvar, it did not exist at init
-                       Con_DPrintf("Cvar_RestoreInitState: Destroying cvar \"%s\"\n", c->name);
-                       // unlink struct from hash
-                       hashindex = CRC_Block((const unsigned char *)c->name, strlen(c->name)) % CVAR_HASHSIZE;
-                       for (cp2 = &cvars->hashtable[hashindex]->cvar;(c2 = *cp2);)
+                       Con_DPrintf("Cvar_RestoreInitState: cvar \"%s\"", c->name);
+                       if (Cvar_Delete(cvars, c->name, NULL))
+                               Con_DPrint("deleted.\n");
+                       else
                        {
-                               if (c2 == c)
-                               {
-                                       *cp2 = cvars->hashtable[hashindex]->next->cvar;
-                                       break;
-                               }
-                               else
-                                       cp2 = &cvars->hashtable[hashindex]->next->cvar;
+                               // In this case, at least reset it to the default.
+                               Con_DPrint("reset.\n");
+                               Cvar_SetQuick(c, c->defstring);
                        }
-                       // unlink struct from main list
-                       *cp = c->next;
-                       // free strings
-                       if (c->defstring)
-                               Z_Free((char *)c->defstring);
-                       if (c->string)
-                               Z_Free((char *)c->string);
-                       if (c->description && c->description != cvar_dummy_description)
-                               Z_Free((char *)c->description);
-                       // free struct
-                       Z_Free(c);
                }
        }
 }
@@ -914,7 +944,7 @@ void Cvar_ResetToDefaults_All_f(cmd_state_t *cmd)
        // restore the default values of all cvars
        for (var = cvars->vars ; var ; var = var->next)
        {
-               if((var->flags & CF_PERSISTENT) == 0)
+               if((var->flags & (CF_PERSISTENT | CF_READONLY)) == 0)
                        Cvar_SetQuick(var, var->defstring);
        }
 }
@@ -926,7 +956,7 @@ void Cvar_ResetToDefaults_NoSaveOnly_f(cmd_state_t *cmd)
        // restore the default values of all cvars
        for (var = cvars->vars ; var ; var = var->next)
        {
-               if ((var->flags & (CF_PERSISTENT | CF_ARCHIVE)) == 0)
+               if ((var->flags & (CF_PERSISTENT | CF_READONLY | CF_ARCHIVE)) == 0)
                        Cvar_SetQuick(var, var->defstring);
        }
 }
@@ -939,7 +969,7 @@ void Cvar_ResetToDefaults_SaveOnly_f(cmd_state_t *cmd)
        // restore the default values of all cvars
        for (var = cvars->vars ; var ; var = var->next)
        {
-               if ((var->flags & (CF_PERSISTENT | CF_ARCHIVE)) == CF_ARCHIVE)
+               if ((var->flags & (CF_PERSISTENT | CF_READONLY | CF_ARCHIVE)) == CF_ARCHIVE)
                        Cvar_SetQuick(var, var->defstring);
        }
 }
@@ -1080,10 +1110,7 @@ void Cvar_SetA_f(cmd_state_t *cmd)
 void Cvar_Del_f(cmd_state_t *cmd)
 {
        cvar_state_t *cvars = cmd->cvars;
-       int neededflags = ~0;
        int i;
-       cvar_t *parent, **link;
-       cvar_t *cvar, *prev;
 
        if(Cmd_Argc(cmd) < 2)
        {
@@ -1091,44 +1118,7 @@ void Cvar_Del_f(cmd_state_t *cmd)
                return;
        }
        for(i = 1; i < Cmd_Argc(cmd); ++i)
-       {
-               cvar = Cvar_FindVarLink(cvars, Cmd_Argv(cmd, i), &parent, &link, &prev, neededflags);
-
-               if(!cvar)
-               {
-                       Con_Printf("%s: %s is not defined\n", Cmd_Argv(cmd, 0), Cmd_Argv(cmd, i));
-                       continue;
-               }
-               if(Cvar_Readonly(cvar, Cmd_Argv(cmd, 0)))
-                       continue;
-               if(!(cvar->flags & CF_ALLOCATED))
-               {
-                       Con_Printf("%s: %s is static and cannot be deleted\n", Cmd_Argv(cmd, 0), cvar->name);
-                       continue;
-               }
-               if(cvar == cvars->vars)
-               {
-                       cvars->vars = cvar->next;
-               }
-               else
-               {
-                       // in this case, prev must be set, otherwise there has been some inconsistensy
-                       // elsewhere already... should I still check for prev != NULL?
-                       prev->next = cvar->next;
-               }
-
-               if(parent)
-                       parent->next = cvar->next;
-               else if(link)
-                       *link = cvar->next;
-               if(cvar->description != cvar_dummy_description)
-                       Z_Free((char *)cvar->description);
-
-               Z_Free((char *)cvar->name);
-               Z_Free((char *)cvar->string);
-               Z_Free((char *)cvar->defstring);
-               Z_Free(cvar);
-       }
+               Cvar_Delete(cvars, Cmd_Argv(cmd, i), Cmd_Argv(cmd, 0));
 }
 
 #ifdef FILLALLCVARSWITHRUBBISH
diff --git a/cvar.h b/cvar.h
index 92ea81cdc54ee26ce172ce63e5ee429cb8d235b9..a0a8716776db1933b1b5b86cc6771b92bbbc19d3 100644 (file)
--- a/cvar.h
+++ b/cvar.h
@@ -64,7 +64,7 @@ struct qfile_s;
 
 typedef struct cvar_s
 {
-       int flags;
+       unsigned flags;
 
        const char *name;
 
@@ -81,7 +81,8 @@ typedef struct cvar_s
        char **aliases;
        int aliases_size;
 
-       struct cvar_s *initstate; // snapshot of cvar during init
+       // this is sufficient for Cvar_RestoreInitState()
+       const char *initstring;
 
        int globaldefindex[3];
        int globaldefindex_stringno[3];
@@ -126,31 +127,31 @@ void Cvar_SetValue (cvar_state_t *cvars, const char *var_name, float value);
 void Cvar_SetQuick (cvar_t *var, const char *value);
 void Cvar_SetValueQuick (cvar_t *var, float value);
 
-float Cvar_VariableValueOr (cvar_state_t *cvars, const char *var_name, float def, int neededflags);
+float Cvar_VariableValueOr (cvar_state_t *cvars, const char *var_name, float def, unsigned neededflags);
 // returns def if not defined
 
-float Cvar_VariableValue (cvar_state_t *cvars, const char *var_name, int neededflags);
+float Cvar_VariableValue (cvar_state_t *cvars, const char *var_name, unsigned neededflags);
 // returns 0 if not defined or non numeric
 
-const char *Cvar_VariableStringOr (cvar_state_t *cvars, const char *var_name, const char *def, int neededflags);
+const char *Cvar_VariableStringOr (cvar_state_t *cvars, const char *var_name, const char *def, unsigned neededflags);
 // returns def if not defined
 
-const char *Cvar_VariableString (cvar_state_t *cvars, const char *var_name, int neededflags);
+const char *Cvar_VariableString (cvar_state_t *cvars, const char *var_name, unsigned neededflags);
 // returns an empty string if not defined
 
-const char *Cvar_VariableDefString (cvar_state_t *cvars, const char *var_name, int neededflags);
+const char *Cvar_VariableDefString (cvar_state_t *cvars, const char *var_name, unsigned neededflags);
 // returns an empty string if not defined
 
-const char *Cvar_VariableDescription (cvar_state_t *cvars, const char *var_name, int neededflags);
+const char *Cvar_VariableDescription (cvar_state_t *cvars, const char *var_name, unsigned neededflags);
 // returns an empty string if not defined
 
-const char *Cvar_CompleteVariable (cvar_state_t *cvars, const char *partial, int neededflags);
+const char *Cvar_CompleteVariable (cvar_state_t *cvars, const char *partial, unsigned neededflags);
 // attempts to match a partial variable name for command line completion
 // returns NULL if nothing fits
 
 void Cvar_PrintHelp(cvar_t *cvar, const char *name, qbool full);
 
-void Cvar_CompleteCvarPrint (cvar_state_t *cvars, const char *partial, int neededflags);
+void Cvar_CompleteCvarPrint (cvar_state_t *cvars, const char *partial, unsigned neededflags);
 
 qbool Cvar_Command (struct cmd_state_s *cmd);
 // called by Cmd_ExecuteString when Cmd_Argv(cmd, 0) doesn't match a known
@@ -170,11 +171,11 @@ void Cvar_WriteVariables (cvar_state_t *cvars, struct qfile_s *f);
 // Writes lines containing "set variable value" for all variables
 // with the archive flag set to true.
 
-cvar_t *Cvar_FindVar(cvar_state_t *cvars, const char *var_name, int neededflags);
-cvar_t *Cvar_FindVarAfter(cvar_state_t *cvars, const char *prev_var_name, int neededflags);
+cvar_t *Cvar_FindVar(cvar_state_t *cvars, const char *var_name, unsigned neededflags);
+cvar_t *Cvar_FindVarAfter(cvar_state_t *cvars, const char *prev_var_name, unsigned neededflags);
 
-int Cvar_CompleteCountPossible(cvar_state_t *cvars, const char *partial, int neededflags);
-const char **Cvar_CompleteBuildList(cvar_state_t *cvars, const char *partial, int neededflags);
+int Cvar_CompleteCountPossible(cvar_state_t *cvars, const char *partial, unsigned neededflags);
+const char **Cvar_CompleteBuildList(cvar_state_t *cvars, const char *partial, unsigned neededflags);
 // Added by EvilTypeGuy - functions for tab completion system
 // Thanks to Fett erich@heintz.com
 // Thanks to taniwha
@@ -193,7 +194,7 @@ void Cvar_Del_f(struct cmd_state_s *cmd);
 
 /// allocates a cvar by name and returns its address,
 /// or merely sets its value if it already exists.
-cvar_t *Cvar_Get(cvar_state_t *cvars, const char *name, const char *value, int flags, const char *newdescription);
+cvar_t *Cvar_Get(cvar_state_t *cvars, const char *name, const char *value, unsigned flags, const char *newdescription);
 
 extern const char *cvar_dummy_description; // ALWAYS the same pointer
 
diff --git a/darkplaces-sdl2-vs2017.vcxproj b/darkplaces-sdl2-vs2017.vcxproj
deleted file mode 100644 (file)
index de80fba..0000000
+++ /dev/null
@@ -1,439 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>\r
-<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">\r
-  <ItemGroup Label="ProjectConfigurations">\r
-    <ProjectConfiguration Include="Debug|Win32">\r
-      <Configuration>Debug</Configuration>\r
-      <Platform>Win32</Platform>\r
-    </ProjectConfiguration>\r
-    <ProjectConfiguration Include="Debug|x64">\r
-      <Configuration>Debug</Configuration>\r
-      <Platform>x64</Platform>\r
-    </ProjectConfiguration>\r
-    <ProjectConfiguration Include="Release|Win32">\r
-      <Configuration>Release</Configuration>\r
-      <Platform>Win32</Platform>\r
-    </ProjectConfiguration>\r
-    <ProjectConfiguration Include="Release|x64">\r
-      <Configuration>Release</Configuration>\r
-      <Platform>x64</Platform>\r
-    </ProjectConfiguration>\r
-  </ItemGroup>\r
-  <PropertyGroup Label="Globals">\r
-    <ProjectGuid>{72D93E63-FDBB-4AA3-B42B-FAADA6D7F5B2}</ProjectGuid>\r
-    <RootNamespace>darkplacessdl2</RootNamespace>\r
-    <Keyword>Win32Proj</Keyword>\r
-    <ProjectName>darkplaces-sdl2-vs2017</ProjectName>\r
-  </PropertyGroup>\r
-  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />\r
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">\r
-    <ConfigurationType>Application</ConfigurationType>\r
-    <PlatformToolset>v141</PlatformToolset>\r
-    <CharacterSet>MultiByte</CharacterSet>\r
-    <WholeProgramOptimization>true</WholeProgramOptimization>\r
-  </PropertyGroup>\r
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">\r
-    <ConfigurationType>Application</ConfigurationType>\r
-    <PlatformToolset>v141</PlatformToolset>\r
-    <CharacterSet>MultiByte</CharacterSet>\r
-  </PropertyGroup>\r
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">\r
-    <ConfigurationType>Application</ConfigurationType>\r
-    <PlatformToolset>v141</PlatformToolset>\r
-    <CharacterSet>MultiByte</CharacterSet>\r
-    <WholeProgramOptimization>true</WholeProgramOptimization>\r
-  </PropertyGroup>\r
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">\r
-    <ConfigurationType>Application</ConfigurationType>\r
-    <PlatformToolset>v141</PlatformToolset>\r
-    <CharacterSet>MultiByte</CharacterSet>\r
-  </PropertyGroup>\r
-  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />\r
-  <ImportGroup Label="ExtensionSettings">\r
-  </ImportGroup>\r
-  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">\r
-    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
-    <Import Project="vs2012_sdl2_win32.props" />\r
-  </ImportGroup>\r
-  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">\r
-    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
-    <Import Project="vs2012_sdl2_win32.props" />\r
-  </ImportGroup>\r
-  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">\r
-    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
-    <Import Project="vs2012_sdl2_win64.props" />\r
-  </ImportGroup>\r
-  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">\r
-    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
-    <Import Project="vs2012_sdl2_win64.props" />\r
-  </ImportGroup>\r
-  <PropertyGroup Label="UserMacros" />\r
-  <PropertyGroup>\r
-    <_ProjectFileVersion>11.0.50727.1</_ProjectFileVersion>\r
-  </PropertyGroup>\r
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">\r
-    <OutDir>$(SolutionDir)\</OutDir>\r
-    <IntDir>$(Configuration)-$(ProjectName)-$(Platform)\</IntDir>\r
-    <LinkIncremental>true</LinkIncremental>\r
-  </PropertyGroup>\r
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">\r
-    <OutDir>$(SolutionDir)\</OutDir>\r
-    <IntDir>$(Configuration)-$(ProjectName)-$(Platform)\</IntDir>\r
-    <LinkIncremental>true</LinkIncremental>\r
-  </PropertyGroup>\r
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">\r
-    <OutDir>$(SolutionDir)\</OutDir>\r
-    <IntDir>$(Configuration)-$(ProjectName)-$(Platform)\</IntDir>\r
-    <LinkIncremental>false</LinkIncremental>\r
-  </PropertyGroup>\r
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">\r
-    <OutDir>$(SolutionDir)\</OutDir>\r
-    <IntDir>$(Configuration)-$(ProjectName)-$(Platform)\</IntDir>\r
-    <LinkIncremental>false</LinkIncremental>\r
-  </PropertyGroup>\r
-  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">\r
-    <ClCompile>\r
-      <Optimization>Disabled</Optimization>\r
-      <PreprocessorDefinitions>CONFIG_MENU;CONFIG_VIDEO_CAPTURE;WIN32;_DEBUG;_WINDOWS;_FILE_OFFSET_BITS=64;__KERNEL_STRICT_NAMES;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r
-      <MinimalRebuild>true</MinimalRebuild>\r
-      <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>\r
-      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>\r
-      <PrecompiledHeader />\r
-      <WarningLevel>Level4</WarningLevel>\r
-      <DebugInformationFormat>EditAndContinue</DebugInformationFormat>\r
-      <DisableSpecificWarnings>4706;4127;4100;4055;4054;4244;4305;4702;%(DisableSpecificWarnings)</DisableSpecificWarnings>\r
-      <MultiProcessorCompilation>true</MultiProcessorCompilation>\r
-      <AdditionalOptions>/wd"4201" %(AdditionalOptions)</AdditionalOptions>\r
-    </ClCompile>\r
-    <Link>\r
-      <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>\r
-      <IgnoreSpecificDefaultLibraries>msvcrt.lib;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>\r
-      <GenerateDebugInformation>true</GenerateDebugInformation>\r
-      <SubSystem>Windows</SubSystem>\r
-      <TargetMachine>MachineX86</TargetMachine>\r
-      <AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>\r
-      <LargeAddressAware>true</LargeAddressAware>\r
-    </Link>\r
-  </ItemDefinitionGroup>\r
-  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">\r
-    <Midl>\r
-      <TargetEnvironment>X64</TargetEnvironment>\r
-    </Midl>\r
-    <ClCompile>\r
-      <Optimization>Disabled</Optimization>\r
-      <PreprocessorDefinitions>CONFIG_MENU;CONFIG_VIDEO_CAPTURE;WIN32;WIN64;_DEBUG;_WINDOWS;_FILE_OFFSET_BITS=64;__KERNEL_STRICT_NAMES;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r
-      <MinimalRebuild>true</MinimalRebuild>\r
-      <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>\r
-      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>\r
-      <PrecompiledHeader />\r
-      <WarningLevel>Level4</WarningLevel>\r
-      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>\r
-      <DisableSpecificWarnings>4706;4127;4100;4055;4054;4244;4305;4702;%(DisableSpecificWarnings)</DisableSpecificWarnings>\r
-      <MultiProcessorCompilation>true</MultiProcessorCompilation>\r
-      <AdditionalOptions>/wd"4201" %(AdditionalOptions)</AdditionalOptions>\r
-    </ClCompile>\r
-    <Link>\r
-      <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>\r
-      <IgnoreSpecificDefaultLibraries>msvcrt.lib;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>\r
-      <GenerateDebugInformation>true</GenerateDebugInformation>\r
-      <SubSystem>Windows</SubSystem>\r
-      <TargetMachine>MachineX64</TargetMachine>\r
-      <AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>\r
-    </Link>\r
-  </ItemDefinitionGroup>\r
-  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">\r
-    <ClCompile>\r
-      <Optimization>MaxSpeed</Optimization>\r
-      <IntrinsicFunctions>true</IntrinsicFunctions>\r
-      <PreprocessorDefinitions>CONFIG_MENU;CONFIG_VIDEO_CAPTURE;WIN32;NDEBUG;_WINDOWS;_FILE_OFFSET_BITS=64;__KERNEL_STRICT_NAMES;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r
-      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>\r
-      <FunctionLevelLinking>true</FunctionLevelLinking>\r
-      <PrecompiledHeader />\r
-      <WarningLevel>Level4</WarningLevel>\r
-      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>\r
-      <DisableSpecificWarnings>4706;4127;4100;4055;4054;4244;4305;4702;%(DisableSpecificWarnings)</DisableSpecificWarnings>\r
-      <MultiProcessorCompilation>true</MultiProcessorCompilation>\r
-      <AdditionalOptions>/wd"4201" %(AdditionalOptions)</AdditionalOptions>\r
-    </ClCompile>\r
-    <Link>\r
-      <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>\r
-      <GenerateDebugInformation>true</GenerateDebugInformation>\r
-      <SubSystem>Windows</SubSystem>\r
-      <OptimizeReferences>true</OptimizeReferences>\r
-      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r
-      <TargetMachine>MachineX86</TargetMachine>\r
-      <AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>\r
-      <LargeAddressAware>true</LargeAddressAware>\r
-    </Link>\r
-  </ItemDefinitionGroup>\r
-  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">\r
-    <Midl>\r
-      <TargetEnvironment>X64</TargetEnvironment>\r
-    </Midl>\r
-    <ClCompile>\r
-      <Optimization>MaxSpeed</Optimization>\r
-      <IntrinsicFunctions>true</IntrinsicFunctions>\r
-      <PreprocessorDefinitions>CONFIG_MENU;CONFIG_VIDEO_CAPTURE;WIN32;WIN64;NDEBUG;_WINDOWS;_FILE_OFFSET_BITS=64;__KERNEL_STRICT_NAMES;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r
-      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>\r
-      <FunctionLevelLinking>true</FunctionLevelLinking>\r
-      <PrecompiledHeader />\r
-      <WarningLevel>Level4</WarningLevel>\r
-      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>\r
-      <DisableSpecificWarnings>4706;4127;4100;4055;4054;4244;4305;4702;%(DisableSpecificWarnings)</DisableSpecificWarnings>\r
-      <MultiProcessorCompilation>true</MultiProcessorCompilation>\r
-      <AdditionalOptions>/wd"4201" %(AdditionalOptions)</AdditionalOptions>\r
-    </ClCompile>\r
-    <Link>\r
-      <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>\r
-      <GenerateDebugInformation>true</GenerateDebugInformation>\r
-      <SubSystem>Windows</SubSystem>\r
-      <OptimizeReferences>true</OptimizeReferences>\r
-      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r
-      <TargetMachine>MachineX64</TargetMachine>\r
-      <AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>\r
-    </Link>\r
-  </ItemDefinitionGroup>\r
-  <ItemGroup>\r
-    <ClCompile Include="bih.c" />\r
-    <ClCompile Include="builddate.c" />\r
-    <ClCompile Include="cap_avi.c" />\r
-    <ClCompile Include="cap_ogg.c" />\r
-    <ClCompile Include="cd_shared.c" />\r
-    <ClCompile Include="cl_cmd.c" />\r
-    <ClCompile Include="cl_collision.c" />\r
-    <ClCompile Include="cl_demo.c" />\r
-    <ClCompile Include="cl_ents.c" />\r
-    <ClCompile Include="cl_ents4.c" />\r
-    <ClCompile Include="cl_ents5.c" />\r
-    <ClCompile Include="cl_ents_nq.c" />\r
-    <ClCompile Include="cl_ents_qw.c" />\r
-    <ClCompile Include="cl_input.c" />\r
-    <ClCompile Include="cl_main.c" />\r
-    <ClCompile Include="cl_parse.c" />\r
-    <ClCompile Include="cl_particles.c" />\r
-    <ClCompile Include="cl_screen.c" />\r
-    <ClCompile Include="cl_video.c" />\r
-    <ClCompile Include="cl_video_libavw.c" />\r
-    <ClCompile Include="clvm_cmds.c" />\r
-    <ClCompile Include="cmd.c" />\r
-    <ClCompile Include="collision.c" />\r
-    <ClCompile Include="com_crc16.c" />\r
-    <ClCompile Include="com_ents.c" />\r
-    <ClCompile Include="com_ents4.c" />\r
-    <ClCompile Include="com_game.c" />\r
-    <ClCompile Include="com_infostring.c" />\r
-    <ClCompile Include="com_msg.c" />\r
-    <ClCompile Include="common.c" />\r
-    <ClCompile Include="console.c" />\r
-    <ClCompile Include="crypto.c">\r
-      <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">/wd"4800" %(AdditionalOptions)</AdditionalOptions>\r
-      <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">/wd"4800" %(AdditionalOptions)</AdditionalOptions>\r
-      <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">/wd"4800" %(AdditionalOptions)</AdditionalOptions>\r
-      <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">/wd"4800" %(AdditionalOptions)</AdditionalOptions>\r
-    </ClCompile>\r
-    <ClCompile Include="csprogs.c" />\r
-    <ClCompile Include="curves.c" />\r
-    <ClCompile Include="cvar.c" />\r
-    <ClCompile Include="dpvsimpledecode.c" />\r
-    <ClCompile Include="filematch.c" />\r
-    <ClCompile Include="fractalnoise.c" />\r
-    <ClCompile Include="fs.c" />\r
-    <ClCompile Include="ft2.c" />\r
-    <ClCompile Include="gl_backend.c" />\r
-    <ClCompile Include="gl_draw.c" />\r
-    <ClCompile Include="gl_rmain.c" />\r
-    <ClCompile Include="gl_rsurf.c" />\r
-    <ClCompile Include="gl_textures.c" />\r
-    <ClCompile Include="hmac.c" />\r
-    <ClCompile Include="host.c" />\r
-    <ClCompile Include="image.c" />\r
-    <ClCompile Include="image_png.c" />\r
-    <ClCompile Include="jpeg.c" />\r
-    <ClCompile Include="keys.c" />\r
-    <ClCompile Include="lhnet.c" />\r
-    <ClCompile Include="libcurl.c" />\r
-    <ClCompile Include="mathlib.c" />\r
-    <ClCompile Include="matrixlib.c" />\r
-    <ClCompile Include="mdfour.c" />\r
-    <ClCompile Include="menu.c" />\r
-    <ClCompile Include="meshqueue.c" />\r
-    <ClCompile Include="mod_skeletal_animatevertices_generic.c" />\r
-    <ClCompile Include="mod_skeletal_animatevertices_sse.c" />\r
-    <ClCompile Include="model_alias.c" />\r
-    <ClCompile Include="model_brush.c" />\r
-    <ClCompile Include="model_shared.c" />\r
-    <ClCompile Include="model_sprite.c" />\r
-    <ClCompile Include="mvm_cmds.c" />\r
-    <ClCompile Include="netconn.c" />\r
-    <ClCompile Include="palette.c" />\r
-    <ClCompile Include="polygon.c" />\r
-    <ClCompile Include="portals.c" />\r
-    <ClCompile Include="protocol.c" />\r
-    <ClCompile Include="prvm_cmds.c" />\r
-    <ClCompile Include="prvm_edict.c" />\r
-    <ClCompile Include="prvm_exec.c" />\r
-    <ClCompile Include="r_explosion.c" />\r
-    <ClCompile Include="r_lightning.c" />\r
-    <ClCompile Include="r_modules.c" />\r
-    <ClCompile Include="r_shadow.c" />\r
-    <ClInclude Include="r_stats.c" />\r
-    <ClCompile Include="r_sky.c" />\r
-    <ClCompile Include="r_sprites.c" />\r
-    <ClCompile Include="sbar.c" />\r
-    <ClCompile Include="snd_main.c" />\r
-    <ClCompile Include="snd_mem.c" />\r
-    <ClCompile Include="snd_mix.c" />\r
-    <ClCompile Include="snd_ogg.c" />\r
-    <ClCompile Include="snd_sdl.c" />\r
-    <ClCompile Include="snd_wav.c" />\r
-    <ClInclude Include="snd_xmp.c" />\r
-    <ClCompile Include="sv_ccmds.c" />\r
-    <ClCompile Include="sv_demo.c" />\r
-    <ClCompile Include="sv_ents.c" />\r
-    <ClCompile Include="sv_ents4.c" />\r
-    <ClCompile Include="sv_ents5.c" />\r
-    <ClCompile Include="sv_ents_nq.c" />\r
-    <ClCompile Include="sv_ents_csqc.c" />\r
-    <ClCompile Include="sv_main.c" />\r
-    <ClCompile Include="sv_move.c" />\r
-    <ClCompile Include="sv_phys.c" />\r
-    <ClCompile Include="sv_save.c" />\r
-    <ClCompile Include="sv_send.c" />\r
-    <ClCompile Include="sv_user.c" />\r
-    <ClCompile Include="svbsp.c" />\r
-    <ClCompile Include="svvm_cmds.c" />\r
-    <ClCompile Include="sys_sdl.c" />\r
-    <ClCompile Include="sys_shared.c" />\r
-    <ClCompile Include="taskqueue.c" />\r
-    <ClCompile Include="thread_sdl.c" />\r
-    <ClCompile Include="utf8lib.c" />\r
-    <ClCompile Include="vid_sdl.c" />\r
-    <ClCompile Include="vid_shared.c" />\r
-    <ClCompile Include="view.c" />\r
-    <ClCompile Include="wad.c" />\r
-    <ClCompile Include="world.c" />\r
-    <ClCompile Include="zone.c" />\r
-  </ItemGroup>\r
-  <ItemGroup>\r
-    <ClInclude Include="bih.h" />\r
-    <ClInclude Include="bspfile.h" />\r
-    <ClInclude Include="cap.h" />\r
-    <ClInclude Include="cap_avi.h" />\r
-    <ClInclude Include="cap_ogg.h" />\r
-    <ClInclude Include="cdaudio.h" />\r
-    <ClInclude Include="cl_collision.h" />\r
-    <ClInclude Include="cl_gecko.h" />\r
-    <ClInclude Include="cl_parse.h" />\r
-    <ClInclude Include="cl_particles.h" />\r
-    <ClInclude Include="cl_screen.h" />\r
-    <ClInclude Include="cl_video.h" />\r
-    <ClInclude Include="cl_video_libavw.h" />\r
-    <ClInclude Include="client.h" />\r
-    <ClInclude Include="clprogdefs.h" />\r
-    <ClInclude Include="clvm_cmds.h" />\r
-    <ClInclude Include="cmd.h" />\r
-    <ClInclude Include="collision.h" />\r
-    <ClInclude Include="com_game.h" />\r
-    <ClInclude Include="com_infostring.h" />\r
-    <ClInclude Include="com_list.h" />\r
-    <ClInclude Include="common.h" />\r
-    <ClInclude Include="console.h" />\r
-    <ClInclude Include="crypto.h" />\r
-    <ClInclude Include="csprogs.h" />\r
-    <ClInclude Include="curves.h" />\r
-    <ClInclude Include="cvar.h" />\r
-    <ClInclude Include="darkplaces.h" />\r
-    <ClInclude Include="dpvsimpledecode.h" />\r
-    <ClInclude Include="draw.h" />\r
-    <ClInclude Include="filematch.h" />\r
-    <ClInclude Include="fs.h" />\r
-    <ClInclude Include="ft2.h" />\r
-    <ClInclude Include="ft2_defs.h" />\r
-    <ClInclude Include="ft2_fontdefs.h" />\r
-    <ClInclude Include="gl_backend.h" />\r
-    <ClInclude Include="glquake.h" />\r
-    <ClInclude Include="hmac.h" />\r
-    <ClInclude Include="host.h" />\r
-    <ClInclude Include="image.h" />\r
-    <ClInclude Include="image_png.h" />\r
-    <ClInclude Include="input.h" />\r
-    <ClInclude Include="jpeg.h" />\r
-    <ClInclude Include="keys.h" />\r
-    <ClInclude Include="lhfont.h" />\r
-    <ClInclude Include="lhnet.h" />\r
-    <ClInclude Include="libcurl.h" />\r
-    <ClInclude Include="mathlib.h" />\r
-    <ClInclude Include="matrixlib.h" />\r
-    <ClInclude Include="mdfour.h" />\r
-    <ClInclude Include="menu.h" />\r
-    <ClInclude Include="meshqueue.h" />\r
-    <ClInclude Include="mod_skeletal_animatevertices_generic.h" />\r
-    <ClInclude Include="mod_skeletal_animatevertices_sse.h" />\r
-    <ClInclude Include="model_alias.h" />\r
-    <ClInclude Include="model_brush.h" />\r
-    <ClInclude Include="model_dpmodel.h" />\r
-    <ClInclude Include="model_iqm.h" />\r
-    <ClInclude Include="model_psk.h" />\r
-    <ClInclude Include="model_q1bsp.h" />\r
-    <ClInclude Include="model_q2bsp.h" />\r
-    <ClInclude Include="model_q3bsp.h" />\r
-    <ClInclude Include="model_vbsp.h" />\r
-    <ClInclude Include="model_shared.h" />\r
-    <ClInclude Include="model_sprite.h" />\r
-    <ClInclude Include="model_zymotic.h" />\r
-    <ClInclude Include="modelgen.h" />\r
-    <ClInclude Include="mprogdefs.h" />\r
-    <ClInclude Include="netconn.h" />\r
-    <ClInclude Include="palette.h" />\r
-    <ClInclude Include="polygon.h" />\r
-    <ClInclude Include="portals.h" />\r
-    <ClInclude Include="prvm_offsets.h" />\r
-    <ClInclude Include="pr_comp.h" />\r
-    <ClInclude Include="progdefs.h" />\r
-    <ClInclude Include="progs.h" />\r
-    <ClInclude Include="progsvm.h" />\r
-    <ClInclude Include="protocol.h" />\r
-    <ClInclude Include="prvm_cmds.h" />\r
-    <ClInclude Include="prvm_execprogram.h" />\r
-    <ClInclude Include="qdefs.h" />\r
-    <ClInclude Include="qstats.h" />\r
-    <ClInclude Include="qtypes.h" />\r
-    <ClInclude Include="quakedef.h" />\r
-    <ClInclude Include="r_lerpanim.h" />\r
-    <ClInclude Include="r_modules.h" />\r
-    <ClInclude Include="r_qshader.h" />\r
-    <ClInclude Include="r_shadow.h" />\r
-    <ClInclude Include="r_stats.h" />\r
-    <ClInclude Include="r_textures.h" />\r
-    <ClInclude Include="render.h" />\r
-    <ClInclude Include="resource.h" />\r
-    <ClInclude Include="sbar.h" />\r
-    <ClInclude Include="screen.h" />\r
-    <ClInclude Include="SDLMain.h" />\r
-    <ClInclude Include="server.h" />\r
-    <ClInclude Include="shader_glsl.h" />\r
-    <ClInclude Include="snd_main.h" />\r
-    <ClInclude Include="snd_ogg.h" />\r
-    <ClInclude Include="snd_wav.h" />\r
-    <ClInclude Include="snd_xmp.h" />\r
-    <ClInclude Include="sound.h" />\r
-    <ClInclude Include="spritegn.h" />\r
-    <ClInclude Include="sv_demo.h" />\r
-    <ClInclude Include="svbsp.h" />\r
-    <ClInclude Include="sys.h" />\r
-    <ClInclude Include="taskqueue.h" />\r
-    <ClInclude Include="thread.h" />\r
-    <ClInclude Include="timing.h" />\r
-    <ClInclude Include="utf8lib.h" />\r
-    <ClInclude Include="vid.h" />\r
-    <ClInclude Include="view.h" />\r
-    <ClInclude Include="wad.h" />\r
-    <ClInclude Include="world.h" />\r
-    <ClInclude Include="zone.h" />\r
-  </ItemGroup>\r
-  <ItemGroup>\r
-    <ResourceCompile Include="darkplaces.rc" />\r
-  </ItemGroup>\r
-  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />\r
-  <ImportGroup Label="ExtensionTargets">\r
-  </ImportGroup>\r
-</Project>
\ No newline at end of file
index 4722fe3331d3f6977b86e891ab775a4916472482..f94592a577ef7ab37488a675c4ae16e78139d116 100644 (file)
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />\r
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">\r
     <ConfigurationType>Application</ConfigurationType>\r
-    <PlatformToolset>v142</PlatformToolset>\r
     <CharacterSet>MultiByte</CharacterSet>\r
     <WholeProgramOptimization>true</WholeProgramOptimization>\r
+    <PlatformToolset>v142</PlatformToolset>\r
   </PropertyGroup>\r
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">\r
     <ConfigurationType>Application</ConfigurationType>\r
-    <PlatformToolset>v142</PlatformToolset>\r
     <CharacterSet>MultiByte</CharacterSet>\r
+    <PlatformToolset>v142</PlatformToolset>\r
   </PropertyGroup>\r
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">\r
     <ConfigurationType>Application</ConfigurationType>\r
-    <PlatformToolset>v142</PlatformToolset>\r
     <CharacterSet>MultiByte</CharacterSet>\r
     <WholeProgramOptimization>true</WholeProgramOptimization>\r
+    <PlatformToolset>v142</PlatformToolset>\r
   </PropertyGroup>\r
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">\r
     <ConfigurationType>Application</ConfigurationType>\r
-    <PlatformToolset>v142</PlatformToolset>\r
     <CharacterSet>MultiByte</CharacterSet>\r
+    <PlatformToolset>v142</PlatformToolset>\r
   </PropertyGroup>\r
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />\r
   <ImportGroup Label="ExtensionSettings">\r
   </ImportGroup>\r
-  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">\r
-    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
-    <Import Project="vs2012_sdl2_win32.props" />\r
-  </ImportGroup>\r
-  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">\r
-    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
-    <Import Project="vs2012_sdl2_win32.props" />\r
-  </ImportGroup>\r
-  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">\r
-    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
-    <Import Project="vs2012_sdl2_win64.props" />\r
-  </ImportGroup>\r
-  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">\r
-    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
-    <Import Project="vs2012_sdl2_win64.props" />\r
-  </ImportGroup>\r
   <PropertyGroup Label="UserMacros" />\r
   <PropertyGroup>\r
     <_ProjectFileVersion>11.0.50727.1</_ProjectFileVersion>\r
       <DisableSpecificWarnings>4706;4127;4100;4055;4054;4244;4305;4702;%(DisableSpecificWarnings)</DisableSpecificWarnings>\r
       <MultiProcessorCompilation>true</MultiProcessorCompilation>\r
       <AdditionalOptions>/wd"4201" %(AdditionalOptions)</AdditionalOptions>\r
+      <LanguageStandard>stdcpp17</LanguageStandard>\r
+      <LanguageStandard_C>stdc17</LanguageStandard_C>\r
     </ClCompile>\r
     <Link>\r
       <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>\r
     <ClCompile>\r
       <Optimization>Disabled</Optimization>\r
       <PreprocessorDefinitions>CONFIG_MENU;CONFIG_VIDEO_CAPTURE;WIN32;WIN64;_DEBUG;_WINDOWS;_FILE_OFFSET_BITS=64;__KERNEL_STRICT_NAMES;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r
-      <MinimalRebuild>true</MinimalRebuild>\r
       <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>\r
       <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>\r
       <PrecompiledHeader />\r
       <DisableSpecificWarnings>4706;4127;4100;4055;4054;4244;4305;4702;%(DisableSpecificWarnings)</DisableSpecificWarnings>\r
       <MultiProcessorCompilation>true</MultiProcessorCompilation>\r
       <AdditionalOptions>/wd"4201" %(AdditionalOptions)</AdditionalOptions>\r
+      <LanguageStandard>stdcpp17</LanguageStandard>\r
+      <LanguageStandard_C>stdc17</LanguageStandard_C>\r
     </ClCompile>\r
     <Link>\r
       <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>\r
       <DisableSpecificWarnings>4706;4127;4100;4055;4054;4244;4305;4702;%(DisableSpecificWarnings)</DisableSpecificWarnings>\r
       <MultiProcessorCompilation>true</MultiProcessorCompilation>\r
       <AdditionalOptions>/wd"4201" %(AdditionalOptions)</AdditionalOptions>\r
+      <LanguageStandard>stdcpp17</LanguageStandard>\r
+      <LanguageStandard_C>stdc17</LanguageStandard_C>\r
     </ClCompile>\r
     <Link>\r
       <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>\r
       <DisableSpecificWarnings>4706;4127;4100;4055;4054;4244;4305;4702;%(DisableSpecificWarnings)</DisableSpecificWarnings>\r
       <MultiProcessorCompilation>true</MultiProcessorCompilation>\r
       <AdditionalOptions>/wd"4201" %(AdditionalOptions)</AdditionalOptions>\r
+      <LanguageStandard_C>stdc17</LanguageStandard_C>\r
+      <LanguageStandard>stdcpp17</LanguageStandard>\r
     </ClCompile>\r
     <Link>\r
       <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>\r
     <ClCompile Include="com_msg.c" />\r
     <ClCompile Include="common.c" />\r
     <ClCompile Include="console.c" />\r
+    <ClCompile Include="convex.c" />\r
     <ClCompile Include="crypto.c">\r
       <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">/wd"4800" %(AdditionalOptions)</AdditionalOptions>\r
       <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">/wd"4800" %(AdditionalOptions)</AdditionalOptions>\r
     <ClCompile Include="model_sprite.c" />\r
     <ClCompile Include="mvm_cmds.c" />\r
     <ClCompile Include="netconn.c" />\r
+    <ClCompile Include="phys.c" />\r
     <ClCompile Include="palette.c" />\r
     <ClCompile Include="polygon.c" />\r
     <ClCompile Include="portals.c" />\r
     <ClInclude Include="modelgen.h" />\r
     <ClInclude Include="mprogdefs.h" />\r
     <ClInclude Include="netconn.h" />\r
+    <ClInclude Include="phys.h" />\r
     <ClInclude Include="palette.h" />\r
     <ClInclude Include="pmove.h" />\r
     <ClInclude Include="polygon.h" />\r
     <ClInclude Include="resource.h" />\r
     <ClInclude Include="sbar.h" />\r
     <ClInclude Include="screen.h" />\r
-    <ClInclude Include="SDLMain.h" />\r
+    <ClInclude Include="SDL.h" />\r
     <ClInclude Include="server.h" />\r
     <ClInclude Include="shader_glsl.h" />\r
     <ClInclude Include="snd_main.h" />\r
     <ClInclude Include="utf8lib.h" />\r
     <ClInclude Include="vid.h" />\r
     <ClInclude Include="view.h" />\r
+    <ClInclude Include="convex.h" />\r
     <ClInclude Include="wad.h" />\r
     <ClInclude Include="world.h" />\r
     <ClInclude Include="zone.h" />\r
   </ItemGroup>\r
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />\r
   <ImportGroup Label="ExtensionTargets">\r
-    <Import Project="packages\sdl2.nuget.redist.2.0.14\build\native\sdl2.nuget.redist.targets" Condition="Exists('packages\sdl2.nuget.redist.2.0.14\build\native\sdl2.nuget.redist.targets')" />\r
-    <Import Project="packages\sdl2.nuget.2.0.14\build\native\sdl2.nuget.targets" Condition="Exists('packages\sdl2.nuget.2.0.14\build\native\sdl2.nuget.targets')" />\r
+    <Import Project="packages\sdl2.nuget.redist.2.30.2\build\native\sdl2.nuget.redist.targets" Condition="Exists('packages\sdl2.nuget.redist.2.30.2\build\native\sdl2.nuget.redist.targets')" />\r
+    <Import Project="packages\sdl2.nuget.2.30.2\build\native\sdl2.nuget.targets" Condition="Exists('packages\sdl2.nuget.2.30.2\build\native\sdl2.nuget.targets')" />\r
   </ImportGroup>\r
   <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">\r
     <PropertyGroup>\r
       <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>\r
     </PropertyGroup>\r
-    <Error Condition="!Exists('packages\sdl2.nuget.redist.2.0.14\build\native\sdl2.nuget.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\sdl2.nuget.redist.2.0.14\build\native\sdl2.nuget.redist.targets'))" />\r
-    <Error Condition="!Exists('packages\sdl2.nuget.2.0.14\build\native\sdl2.nuget.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\sdl2.nuget.2.0.14\build\native\sdl2.nuget.targets'))" />\r
+    <Error Condition="!Exists('packages\sdl2.nuget.redist.2.30.2\build\native\sdl2.nuget.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\sdl2.nuget.redist.2.30.2\build\native\sdl2.nuget.redist.targets'))" />\r
+    <Error Condition="!Exists('packages\sdl2.nuget.2.30.2\build\native\sdl2.nuget.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\sdl2.nuget.2.30.2\build\native\sdl2.nuget.targets'))" />\r
   </Target>\r
 </Project>
\ No newline at end of file
diff --git a/darkplaces-vs2017.sln b/darkplaces-vs2017.sln
deleted file mode 100644 (file)
index a3e2748..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-\r
-Microsoft Visual Studio Solution File, Format Version 12.00\r
-# Visual Studio 14\r
-VisualStudioVersion = 14.0.25420.1\r
-MinimumVisualStudioVersion = 10.0.40219.1\r
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "darkplaces-sdl2-vs2017", "darkplaces-sdl2-vs2017.vcxproj", "{72D93E63-FDBB-4AA3-B42B-FAADA6D7F5B2}"\r
-EndProject\r
-Global\r
-       GlobalSection(SolutionConfigurationPlatforms) = preSolution\r
-               Debug|Win32 = Debug|Win32\r
-               Debug|x64 = Debug|x64\r
-               Release|Win32 = Release|Win32\r
-               Release|x64 = Release|x64\r
-       EndGlobalSection\r
-       GlobalSection(ProjectConfigurationPlatforms) = postSolution\r
-               {6E3D4311-BB84-4EB7-8C6C-10AA3D249C28}.Debug|Win32.ActiveCfg = Debug|Win32\r
-               {6E3D4311-BB84-4EB7-8C6C-10AA3D249C28}.Debug|Win32.Build.0 = Debug|Win32\r
-               {6E3D4311-BB84-4EB7-8C6C-10AA3D249C28}.Debug|x64.ActiveCfg = Debug|x64\r
-               {6E3D4311-BB84-4EB7-8C6C-10AA3D249C28}.Debug|x64.Build.0 = Debug|x64\r
-               {6E3D4311-BB84-4EB7-8C6C-10AA3D249C28}.Release|Win32.ActiveCfg = Release|Win32\r
-               {6E3D4311-BB84-4EB7-8C6C-10AA3D249C28}.Release|Win32.Build.0 = Release|Win32\r
-               {6E3D4311-BB84-4EB7-8C6C-10AA3D249C28}.Release|x64.ActiveCfg = Release|x64\r
-               {6E3D4311-BB84-4EB7-8C6C-10AA3D249C28}.Release|x64.Build.0 = Release|x64\r
-               {389AE334-D907-4069-90B3-F0551B3EFDE9}.Debug|Win32.ActiveCfg = Debug|Win32\r
-               {389AE334-D907-4069-90B3-F0551B3EFDE9}.Debug|Win32.Build.0 = Debug|Win32\r
-               {389AE334-D907-4069-90B3-F0551B3EFDE9}.Debug|x64.ActiveCfg = Debug|x64\r
-               {389AE334-D907-4069-90B3-F0551B3EFDE9}.Debug|x64.Build.0 = Debug|x64\r
-               {389AE334-D907-4069-90B3-F0551B3EFDE9}.Release|Win32.ActiveCfg = Release|Win32\r
-               {389AE334-D907-4069-90B3-F0551B3EFDE9}.Release|Win32.Build.0 = Release|Win32\r
-               {389AE334-D907-4069-90B3-F0551B3EFDE9}.Release|x64.ActiveCfg = Release|x64\r
-               {389AE334-D907-4069-90B3-F0551B3EFDE9}.Release|x64.Build.0 = Release|x64\r
-               {72D93E63-FDBB-4AA3-B42B-FAADA6D7F5B2}.Debug|Win32.ActiveCfg = Debug|Win32\r
-               {72D93E63-FDBB-4AA3-B42B-FAADA6D7F5B2}.Debug|Win32.Build.0 = Debug|Win32\r
-               {72D93E63-FDBB-4AA3-B42B-FAADA6D7F5B2}.Debug|x64.ActiveCfg = Debug|x64\r
-               {72D93E63-FDBB-4AA3-B42B-FAADA6D7F5B2}.Debug|x64.Build.0 = Debug|x64\r
-               {72D93E63-FDBB-4AA3-B42B-FAADA6D7F5B2}.Release|Win32.ActiveCfg = Release|Win32\r
-               {72D93E63-FDBB-4AA3-B42B-FAADA6D7F5B2}.Release|Win32.Build.0 = Release|Win32\r
-               {72D93E63-FDBB-4AA3-B42B-FAADA6D7F5B2}.Release|x64.ActiveCfg = Release|x64\r
-               {72D93E63-FDBB-4AA3-B42B-FAADA6D7F5B2}.Release|x64.Build.0 = Release|x64\r
-       EndGlobalSection\r
-       GlobalSection(SolutionProperties) = preSolution\r
-               HideSolutionNode = FALSE\r
-       EndGlobalSection\r
-EndGlobal\r
index 512de65f1198969020c34d9f2a1bdc9e05cdfc1c..0d04dd45eca661bb1e546de1850424f7ad96c749 100644 (file)
@@ -58,13 +58,4 @@ extern char engineversion[128];
 #include "mathlib.h"
 #include "matrixlib.h"
 
-extern cvar_t developer;
-extern cvar_t developer_entityparsing;
-extern cvar_t developer_extra;
-extern cvar_t developer_insane;
-extern cvar_t developer_loadfile;
-extern cvar_t developer_loading;
-extern cvar_t host_isclient;
-extern cvar_t sessionid;
-
 #endif
index f4aa574ca4bf3ea3287dabfe1842c394eefa9c64..b5c057462ca87037c2a069f79f849cad0d2d735a 100644 (file)
@@ -42,13 +42,17 @@ float               trace_inopen;
 float          trace_inwater;
 
 //
-// required prog functions
+// prog functions called by engine
 //
-void()         CSQC_Init;
-void()         CSQC_Shutdown;
-float(float f, float t, float n)       CSQC_InputEvent;
-void(float w, float h)         CSQC_UpdateView;
-float(string s)        CSQC_ConsoleCommand;
+void  CSQC_Init(float apilevel, string enginename, float engineversion);
+void  CSQC_Shutdown();
+float CSQC_InputEvent(float evtype, float scanx, float chary);
+void  CSQC_UpdateView(float vid_width, float vid_height, float notmenu); // required for EXT_CSQC (preferred)
+float CSQC_ConsoleCommand(string cmdstr);
+#ifdef CSQC_SIMPLE // hud-only CSQC
+       void CSQC_DrawHud(vector virtsize, float showscores); // required for CSQC_SIMPLE (fallback)
+       void CSQC_DrawScores(vector virtsize, float showscores);
+#endif
 
 //these fields are read and set by the default player physics
 vector         pmove_org;
@@ -336,7 +340,7 @@ void(entity e, string m) setmodel = #3;
 void(entity e, vector min, vector max) setsize = #4;
 
 void() break_to_debugger = #6;
-float() random = #7;
+float() random = #7; // Returns a random number > 0 and < 1
 void(entity e, float chan, string samp, float volume, float atten, ...) sound = #8;
 vector(vector v) normalize = #9;
 void(string e) error = #10;
@@ -430,7 +434,7 @@ void(float fhandle) fclose = #111;
 string(float fhandle) fgets = #112;
 void(float fhandle, string s) fputs = #113;
 float(string s) strlen = #114;
-string(...) strcat = #115;
+string(string s, string...) strcat = #115;
 string(string s, float start, float length) substring = #116;
 vector(string) stov = #117;
 string(string s) strzone = #118;
@@ -1183,6 +1187,16 @@ float CVAR_TYPEFLAG_READONLY = 32;
 //When caseinsensitive is set, the CRC is calculated of the lower cased string.
 float(float caseinsensitive, string s, ...) crc16 = #494;
 
+//DP_QC_WHICHPACK
+//idea: divVerent
+//darkplaces implementation: divVerent
+//builtin definitions:
+string(string filename) whichpack = #503;
+//description:
+//for files in a pak/pk3/whatever, returns the pack's file name in FRIK_FILE name space.
+//for physical files, returns "".
+//in case of error, returns string_null.
+
 //DP_QC_URI_ESCAPE
 //idea: divVerent
 //darkplaces implementation: divVerent
@@ -1433,12 +1447,23 @@ void(float fh, entity e) writetofile = #606;
 float(string s) isfunction = #607;
 void(entity e, string s) parseentitydata = #608;
 
+//DP_QC_FINDCHAIN_TOFIELD
+//see: dpextensions.qc
+entity(vector org, float rad, .entity tofield) findradius_tofield = #22;
+entity(.string fld, string match, .entity tofield) findchain_tofield = #402;
+entity(.float fld, float match, .entity tofield) findchainflags_tofield = #450;
+entity(.float fld, float match, .entity tofield) findchainfloat_tofield = #403;
+
 //DP_COVERAGE
 //idea: divVerent
 //darkplaces implementation: divVerent
 //function definitions:
 void coverage() = #642;  // Reports a coverage event. The engine counts for each of the calls to this builtin whether it has been called.
 
+//DP_RM_CLIPGROUP
+//see: dpextensions.qc
+.float clipgroup;
+
 //DP_QC_FS_SEARCH_PACKFILE
 //idea: Mario
 //darkplaces implementation: Mario
@@ -1448,6 +1473,15 @@ float(string pattern, float caseinsensitive, float quiet, string packfile) searc
 //extension to search_begin (DP_QC_FS_SEARCH), performs a filename search with the specified pattern (for example "maps/*.bsp") and stores the results in a search slot (minimum of 128 supported by any engine with this extension), the other functions take this returned search slot number, be sure to search_free when done (they are also freed on progs reload).
 //only searches for files within the specified packfile, which is expected to match the results of whichpack().
 
+//DP_QC_FINDBOX
+//see: dpextensions.qc
+entity(vector mins, vector maxs) findbox = #566;
+entity(vector mins, vector maxs, .entity tofield) findbox_tofield = #566;
+
+//DP_QC_NUDGEOUTOFSOLID
+//see: dpextensions.qc
+float(entity ent) nudgeoutofsolid = #567;
+
 // assorted builtins
 const float            STAT_MOVEVARS_TICRATE           = 240;
 const float            STAT_MOVEVARS_TIMESCALE         = 241;
index 48045a202faf22ed1b7bdcbf811db9d113d7752e..3978698cec36641fb0a302245da3e54cbf7fa60b 100644 (file)
@@ -35,7 +35,7 @@ float(string s) checkextension = #99;
 //field definitions:
 .float buttonchat;
 //description:
-//true if the player is currently chatting (in messagemode, menus or console)
+//true if the player is currently chatting (in messagemode, menus or console) or if their game window is not active.
 
 //DP_BUTTONUSE
 //idea: id Software
@@ -763,10 +763,10 @@ entity(.string fld, string match) findchain = #402;
 //idea: divVerent
 //darkplaces implementation: divVerent
 //builtin definitions:
-entity(.string fld, float match, .entity tofield) findradius_tofield = #22;
+entity(vector org, float rad, .entity tofield) findradius_tofield = #22;
 entity(.string fld, string match, .entity tofield) findchain_tofield = #402;
-entity(.string fld, float match, .entity tofield) findchainflags_tofield = #450;
-entity(.string fld, float match, .entity tofield) findchainfloat_tofield = #403;
+entity(.float fld, float match, .entity tofield) findchainflags_tofield = #450;
+entity(.float fld, float match, .entity tofield) findchainfloat_tofield = #403;
 //description:
 //similar to findchain() etc, but stores the chain into .tofield instead of .chain
 //actually, the .entity tofield is an optional field of the the existing findchain* functions
@@ -2140,7 +2140,7 @@ void(float fhandle) fclose = #111; // closes a file
 string(float fhandle) fgets = #112; // reads a line of text from the file and returns as a tempstring
 void(float fhandle, string s, ...) fputs = #113; // writes a line of text to the end of the file
 float(string s) strlen = #114; // returns how many characters are in a string
-string(string s1, string s2, ...) strcat = #115; // concatenates two or more strings (for example "abc", "def" would return "abcdef") and returns as a tempstring
+string(string s, string...) strcat = #115; // concatenates 1 to 8 strings (for example "abc", "def" would return "abcdef") and returns as a tempstring
 string(string s, float start, float length) substring = #116; // returns a section of a string as a tempstring - see FTE_STRINGS for enhanced version
 vector(string s) stov = #117; // returns vector value from a string
 string(string s, ...) strzone = #118; // makes a copy of a string into the string zone and returns it, this is often used to keep around a tempstring for longer periods of time (tempstrings are replaced often)
@@ -2633,12 +2633,36 @@ float(string pattern, float caseinsensitive, float quiet, string packfile) searc
 //extension to search_begin (DP_QC_FS_SEARCH), performs a filename search with the specified pattern (for example "maps/*.bsp") and stores the results in a search slot (minimum of 128 supported by any engine with this extension), the other functions take this returned search slot number, be sure to search_free when done (they are also freed on progs reload).
 //only searches for files within the specified packfile, which is expected to match the results of whichpack().
 
-//EXT_CSQC (registercommand for SSQC)
+//EXT_CSQC (registercommand for CSQC, now also available in SVQC)
 //idea: probably Spoike
 //darkplaces implementation: Cloudwalk
 //builtin definitions:
 void(string cmdname) registercommand = #352;
+//engine-called QC prototypes:
+//float CSQC_ConsoleCommand(string command);
+//float ConsoleCmd(string command);
+//description:
+//Adds a new console command which will take priority over a previous command of the same name (including engine commands) and in CSQC is removed when the VM shuts down. This will call CSQC_ConsoleCommand(string command) or ConsoleCmd(string command) in SVQC.  Return value should be true if QC handled the command, otherwise return false to have the engine handle it.
+
+//DP_QC_FINDBOX
+//idea: Mario
+//darkplaces implementation: bones_was_here
+//builtin definitions:
+entity(vector mins, vector maxs) findbox = #566;
+entity(vector mins, vector maxs, .entity tofield) findbox_tofield = #566;
 //description:
-//the registercommand builtin but the server can use it
+//Returns a chain of entities that are touching a box (a simpler findradius); supports DP_QC_FINDCHAIN_TOFIELD
+
+//DP_QC_NUDGEOUTOFSOLID
+//idea: LadyHavoc, bones_was_here
+//darkplaces implementation: LadyHavoc, bones_was_here
+//builtin definitions:
+float(entity ent) nudgeoutofsolid = #567;
+//cvar definitions:
+//sv_gameplayfix_nudgeoutofsolid_separation
+//description:
+//Attempts to move a stuck entity out of solid brushes, returning 1 if successful, 0 if it remains stuck, -1 if it wasn't stuck.
+//Note: makes only one tracebox call if the entity isn't stuck, so don't call tracebox just to see if you should call nudgeoutofsolid.
+
 
 float(float dividend, float divisor) mod = #245;
index 63d2c6388e8447e90e46445abbe65506e93e074b..93bdff12d6fbe9a0a455f214651c3d59403d3140 100644 (file)
@@ -179,9 +179,10 @@ float      vlen(vector v)                  = #9;
 float          vectoyaw(vector v)              = #10;
 vector         vectoangles(vector v)           = #11;
 
-float  random(void)  = #12;
+float  random(void)  = #12; // Returns a random number > 0 and < 1
 
 void   cmd(string command, ...) = #13;
+void localcmd(string command, ...) = #13;
 
 // cvar cmds
 
@@ -246,7 +247,7 @@ string      fgets(float fhandle)  = #50;
 void   fputs(float fhandle, string s)  = #51;
 
 float  strlen(string s)  = #52;
-string strcat(string s1,string s2,...)  = #53;
+string strcat(string s, string...)  = #53;
 string substring(string s, float start, float length)  = #54;
 
 vector stov(string s)  = #55;
@@ -477,6 +478,16 @@ string(string digest, string data, ...) digest_hex = #639;
 //if the given digest is not supported, string_null is returned
 //the digest string is matched case sensitively, use "MD4", not "md4"!
 
+//DP_QC_WHICHPACK
+//idea: divVerent
+//darkplaces implementation: divVerent
+//builtin definitions:
+string(string filename) whichpack = #503;
+//description:
+//for files in a pak/pk3/whatever, returns the pack's file name in FRIK_FILE name space.
+//for physical files, returns "".
+//in case of error, returns string_null.
+
 //DP_QC_URI_ESCAPE
 //idea: div0
 //darkplaces implementation: div0
index 68f161ce289f47e4dc805e01c84a9d04820b9d52..937f0a72c14523c875735cafc6d1aab259458493 100644 (file)
@@ -236,7 +236,7 @@ float       FL_INWATER                              = 16;   // for enter / leave water splash
 float  FL_MONSTER                              = 32;
 float  FL_GODMODE                              = 64;   // player cheat
 float  FL_NOTARGET                             = 128;  // player cheat
-float  FL_ITEM                                 = 256;  // extra wide size for bonus items
+float  FL_ITEM                                 = 256;  // extra wide size for bonus items IF sv_legacy_bbox_expand is 1
 float  FL_ONGROUND                             = 512;  // standing on something
 float  FL_PARTIALGROUND                = 1024; // not all corners are valid
 float  FL_WATERJUMP                    = 2048; // player jumping out of water
@@ -403,7 +403,7 @@ void(entity e, string m) setmodel   = #3;           // set movetype and solid first
 void(entity e, vector min, vector max) setsize = #4;
 // #5 was removed
 void() break_to_debugger                                               = #6;
-float() random                                         = #7;           // returns 0 - 1
+float() random                                         = #7;   // Returns a random number > 0 and < 1
 void(entity e, float chan, string samp, float volume, ...) sound = #8;
 vector(vector v) normalize                     = #9;
 void(string e, ...) error                              = #10;
index 011c95bf4d32260c3078aaebec28fa304779b10f..244387ab6dc55f5edebc912d82dc782ae8e91413 100644 (file)
@@ -397,7 +397,7 @@ void *dpvsimpledecode_open(clvideo_t *video, char *filename, const char **errors
                                                                        sfx_t* sfx;
 
                                                                        FS_StripExtension(filename, wavename, namelen);
-                                                                       strlcat(wavename, ".wav", namelen);
+                                                                       dp_strlcat(wavename, ".wav", namelen);
                                                                        sfx = S_PrecacheSound (wavename, false, false);
                                                                        if (sfx != NULL)
                                                                                s->sndchan = S_StartSound (-1, 0, sfx, vec3_origin, 1.0f, 0);
diff --git a/draw.h b/draw.h
index 562b8d1ba21fe9494678d691c76e59d07dde50ec..8496bb73a1cf1e1dd67a15e0972419c55cf7d7f3 100644 (file)
--- a/draw.h
+++ b/draw.h
@@ -93,10 +93,12 @@ typedef struct ft2_settings_s
 
 #define MAX_FONT_SIZES 16
 #define MAX_FONT_FALLBACKS 3
+#define MAX_FONT_CMDLINE MAX_QPATH * (MAX_FONT_FALLBACKS + 1)
 typedef struct dp_font_s
 {
        cachepic_t *pic;
        float width_of[256]; // width_of[0] == max width of any char; 1.0f is base width (1/16 of texture width); therefore, all widths have to be <= 1 (does not include scale)
+       float width_of_ft2[MAX_FONT_SIZES][256];
        float maxwidth; // precalculated max width of the font (includes scale)
        char texpath[MAX_QPATH];
        char title[MAX_QPATH];
@@ -108,6 +110,8 @@ typedef struct dp_font_s
        struct ft2_font_s *ft2;
 
        ft2_settings_t settings;
+
+       char cmdline[MAX_FONT_CMDLINE];
 }
 dp_font_t;
 
index e06439614c2174927168128adf5b1fcbe8fa268d..38ee0372e940211702ecb710b3504852bd5c26b3 100644 (file)
@@ -7,6 +7,10 @@
 
 #include "darkplaces.h"
 
+#ifdef WIN32
+#include "utf8lib.h"
+#endif
+
 // LadyHavoc: some portable directory listing code I wrote for lmp2pcx, now used in darkplaces to load id1/*.pak and such...
 
 int matchpattern(const char *in, const char *pattern, int caseinsensitive)
@@ -164,20 +168,31 @@ static void adddirentry(stringlist_t *list, const char *path, const char *name)
 #ifdef WIN32
 void listdirectory(stringlist_t *list, const char *basepath, const char *path)
 {
-       char pattern[4096];
-       WIN32_FIND_DATA n_file;
+       #define BUFSIZE 4096
+       char pattern[BUFSIZE] = {0};
+       wchar patternw[BUFSIZE] = {0};
+       char filename[BUFSIZE] = {0};
+       wchar *filenamew;
+       int lenw = 0;
+       WIN32_FIND_DATAW n_file;
        HANDLE hFile;
-       strlcpy (pattern, basepath, sizeof(pattern));
-       strlcat (pattern, path, sizeof (pattern));
-       strlcat (pattern, "*", sizeof (pattern));
+       dp_strlcpy(pattern, basepath, sizeof(pattern));
+       dp_strlcat(pattern, path, sizeof (pattern));
+       dp_strlcat(pattern, "*", sizeof (pattern));
+       fromwtf8(pattern, strlen(pattern), patternw, BUFSIZE);
        // ask for the directory listing handle
-       hFile = FindFirstFile(pattern, &n_file);
+       hFile = FindFirstFileW(patternw, &n_file);
        if(hFile == INVALID_HANDLE_VALUE)
                return;
        do {
-               adddirentry(list, path, n_file.cFileName);
-       } while (FindNextFile(hFile, &n_file) != 0);
+               filenamew = n_file.cFileName;
+               lenw = 0;
+               while(filenamew[lenw] != 0) ++lenw;
+               towtf8(filenamew, lenw, filename, BUFSIZE);
+               adddirentry(list, path, filename);
+       } while (FindNextFileW(hFile, &n_file) != 0);
        FindClose(hFile);
+       #undef BUFSIZE
 }
 #else
 void listdirectory(stringlist_t *list, const char *basepath, const char *path)
diff --git a/fs.c b/fs.c
index f8266c71a2a7e7e10b4ee3c21f1d533f6e657010..8e95b43c40b8538b76babfe8611efdafacf61208 100644 (file)
--- a/fs.c
+++ b/fs.c
 #include "fs.h"
 #include "wad.h"
 
+#ifdef WIN32
+#include "utf8lib.h"
+#endif
+
 // Win32 requires us to add O_BINARY, but the other OSes don't have it
 #ifndef O_BINARY
 # define O_BINARY 0
@@ -105,9 +109,69 @@ static filedesc_t FILEDESC_DUP(const char *filename, filedesc_t fd) {
 }
 #endif
 
+
+/* This code seems to have originally been written with the assumption that
+ * read(..., n) returns n on success. This is not the case (refer to
+ * <https://pubs.opengroup.org/onlinepubs/9699919799/functions/read.html>).
+ * Ditto for write.
+ */
+
+/*
+====================
+ReadAll
+
+Read exactly length bytes from fd into buf. If end of file is reached,
+the number of bytes read is returned. If an error occurred, that error
+is returned. Note that if an error is returned, any previously read
+data is lost.
+====================
+*/
+static fs_offset_t ReadAll(const filedesc_t fd, void *const buf, const size_t length)
+{
+       char *const p = (char *)buf;
+       size_t cursor = 0;
+       do
+       {
+               const fs_offset_t result = FILEDESC_READ(fd, p + cursor, length - cursor);
+               if (result < 0) // Error
+                       return result;
+               if (result == 0) // EOF
+                       break;
+               cursor += result;
+       } while (cursor < length);
+       return cursor;
+}
+
+/*
+====================
+WriteAll
+
+Write exactly length bytes to fd from buf.
+If an error occurred, that error is returned.
+====================
+*/
+static fs_offset_t WriteAll(const filedesc_t fd, const void *const buf, const size_t length)
+{
+       const char *const p = (const char *)buf;
+       size_t cursor = 0;
+       do
+       {
+               const fs_offset_t result = FILEDESC_WRITE(fd, p + cursor, length - cursor);
+               if (result < 0) // Error
+                       return result;
+               cursor += result;
+       } while (cursor < length);
+       return cursor;
+}
+
+#undef FILEDESC_READ
+#define FILEDESC_READ ReadAll
+#undef FILEDESC_WRITE
+#define FILEDESC_WRITE WriteAll
+
 /** \page fs File System
 
-All of Quake's data access is through a hierchal file system, but the contents
+All of Quake's data access is through a hierarchical file system, the contents
 of the file system can be transparently merged from several sources.
 
 The "base directory" is the path to the directory holding the quake.exe and
@@ -119,9 +183,8 @@ directory is only used during filesystem initialization.
 The "game directory" is the first tree on the search path and directory that
 all generated files (savegames, screenshots, demos, config files) will be
 saved to.  This can be overridden with the "-game" command line parameter.
-The game directory can never be changed while quake is executing.  This is a
-precaution against having a malicious server instruct clients to write files
-over areas they shouldn't.
+If multiple "-game <gamedir>" args are passed the last one is the "primary"
+and files will be saved there, the rest are read-only.
 
 */
 
@@ -318,9 +381,10 @@ typedef struct pack_s
        char filename [MAX_OSPATH];
        char shortname [MAX_QPATH];
        filedesc_t handle;
-       int ignorecase;  ///< PK3 ignores case
+       qbool ignorecase;  ///< PK3 ignores case
        int numfiles;
        qbool vpack;
+       qbool dlcache;
        packfile_t *files;
 } pack_t;
 //@}
@@ -347,7 +411,7 @@ void FS_Dir_f(cmd_state_t *cmd);
 void FS_Ls_f(cmd_state_t *cmd);
 void FS_Which_f(cmd_state_t *cmd);
 
-static searchpath_t *FS_FindFile (const char *name, int* index, qbool quiet);
+static searchpath_t *FS_FindFile (const char *name, int *index, const char **canonicalname, qbool quiet);
 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
                                                                        fs_offset_t offset, fs_offset_t packsize,
                                                                        fs_offset_t realsize, int flags);
@@ -384,6 +448,7 @@ int fs_all_gamedirs_count = 0;
 
 cvar_t scr_screenshot_name = {CF_CLIENT | CF_PERSISTENT, "scr_screenshot_name","dp", "prefix name for saved screenshots (changes based on -game commandline, as well as which game mode is running; the date is encoded using strftime escapes)"};
 cvar_t fs_empty_files_in_pack_mark_deletions = {CF_CLIENT | CF_SERVER, "fs_empty_files_in_pack_mark_deletions", "0", "if enabled, empty files in a pak/pk3 count as not existing but cancel the search in further packs, effectively allowing patch pak/pk3 files to 'delete' files"};
+cvar_t fs_unload_dlcache = {CF_CLIENT, "fs_unload_dlcache", "1", "if enabled, unload dlcache's loaded pak/pk3 files when changing server and/or map WARNING: disabling unloading can cause servers to override assets of other servers, \"memory leaking\" by dlcache assets never unloading and many more issues"};
 cvar_t cvar_fs_gamedir = {CF_CLIENT | CF_SERVER | CF_READONLY | CF_PERSISTENT, "fs_gamedir", "", "the list of currently selected gamedirs (use the 'gamedir' command to change this)"};
 
 
@@ -437,10 +502,10 @@ static dllhandle_t zlib_dll = NULL;
 #endif
 
 #ifdef WIN32
-static HRESULT (WINAPI *qSHGetFolderPath) (HWND hwndOwner, int nFolder, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath);
+static HRESULT (WINAPI *qSHGetFolderPath) (HWND hwndOwner, int nFolder, HANDLE hToken, DWORD dwFlags, LPWSTR pszPath);
 static dllfunction_t shfolderfuncs[] =
 {
-       {"SHGetFolderPathA", (void **) &qSHGetFolderPath},
+       {"SHGetFolderPathW", (void **) &qSHGetFolderPath},
        {NULL, NULL}
 };
 static const char* shfolderdllnames [] =
@@ -450,7 +515,7 @@ static const char* shfolderdllnames [] =
 };
 static dllhandle_t shfolder_dll = NULL;
 
-const GUID qFOLDERID_SavedGames = {0x4C5C32FF, 0xBB9D, 0x43b0, {0xB5, 0xB4, 0x2D, 0x72, 0xE5, 0x4E, 0xAA, 0xA4}}; 
+const GUID qFOLDERID_SavedGames = {0x4C5C32FF, 0xBB9D, 0x43b0, {0xB5, 0xB4, 0x2D, 0x72, 0xE5, 0x4E, 0xAA, 0xA4}};
 #define qREFKNOWNFOLDERID const GUID *
 #define qKF_FLAG_CREATE 0x8000
 #define qKF_FLAG_NO_ALIAS 0x1000
@@ -682,7 +747,7 @@ static int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd)
                        return -1;
                }
 
-               namesize = BuffLittleShort (&ptr[28]);  // filename length
+               namesize = (unsigned short)BuffLittleShort (&ptr[28]);  // filename length
 
                // Check encryption, compression, and attributes
                // 1st uint8  : general purpose bit flag
@@ -699,7 +764,7 @@ static int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd)
                if ((ptr[8] & 0x21) == 0 && (ptr[38] & 0x18) == 0)
                {
                        // Still enough bytes for the name?
-                       if (namesize < 0 || remaining < namesize || namesize >= (int)sizeof (*pack->files))
+                       if (remaining < namesize || namesize >= (int)sizeof (*pack->files))
                        {
                                Mem_Free (central_dir);
                                return -1;
@@ -743,7 +808,7 @@ static int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd)
                // Skip the name, additionnal field, and comment
                // 1er uint16 : extra field length
                // 2eme uint16 : file comment length
-               count = namesize + BuffLittleShort (&ptr[30]) + BuffLittleShort (&ptr[32]);
+               count = namesize + (unsigned short)BuffLittleShort (&ptr[30]) + (unsigned short)BuffLittleShort (&ptr[32]);
                ptr += ZIP_CDIR_CHUNK_BASE_SIZE + count;
                remaining -= count;
        }
@@ -798,7 +863,7 @@ static pack_t *FS_LoadPackPK3FromFD (const char *packfile, filedesc_t packhandle
        // Create a package structure in memory
        pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
        pack->ignorecase = true; // PK3 ignores case
-       strlcpy (pack->filename, packfile, sizeof (pack->filename));
+       dp_strlcpy (pack->filename, packfile, sizeof (pack->filename));
        pack->handle = packhandle;
        pack->numfiles = eocd.nbentries;
        pack->files = (packfile_t *)Mem_Alloc(fs_mempool, eocd.nbentries * sizeof(packfile_t));
@@ -916,7 +981,7 @@ static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
        memmove (pfile + 1, pfile, (pack->numfiles - left) * sizeof (*pfile));
        pack->numfiles++;
 
-       strlcpy (pfile->name, name, sizeof (pfile->name));
+       dp_strlcpy (pfile->name, name, sizeof (pfile->name));
        pfile->offset = offset;
        pfile->packsize = packsize;
        pfile->realsize = realsize;
@@ -925,14 +990,30 @@ static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
        return pfile;
 }
 
+#if WIN32
+#define WSTRBUF 4096
+static inline int wstrlen(wchar *wstr)
+{
+       int len = 0;
+       while (wstr[len] != 0 && len < WSTRBUF)
+               ++len;
+       return len;
+}
+#define widen(str, wstr) fromwtf8(str, strlen(str), wstr, WSTRBUF)
+#define narrow(wstr, str) towtf8(wstr, wstrlen(wstr), str, WSTRBUF)
+#endif
 
 static void FS_mkdir (const char *path)
 {
+#if WIN32
+       wchar pathw[WSTRBUF] = {0};
+#endif
        if(Sys_CheckParm("-readonly"))
                return;
 
 #if WIN32
-       if (_mkdir (path) == -1)
+       widen(path, pathw);
+       if (_wmkdir (pathw) == -1)
 #else
        if (mkdir (path, 0777) == -1)
 #endif
@@ -944,7 +1025,6 @@ static void FS_mkdir (const char *path)
        }
 }
 
-
 /*
 ============
 FS_CreatePath
@@ -1059,7 +1139,7 @@ static pack_t *FS_LoadPackPAK (const char *packfile)
 
        pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
        pack->ignorecase = true; // PAK is sensitive in Quake1 but insensitive in Quake2
-       strlcpy (pack->filename, packfile, sizeof (pack->filename));
+       dp_strlcpy (pack->filename, packfile, sizeof (pack->filename));
        pack->handle = packhandle;
        pack->numfiles = 0;
        pack->files = (packfile_t *)Mem_Alloc(fs_mempool, numpackfiles * sizeof(packfile_t));
@@ -1095,7 +1175,7 @@ static pack_t *FS_LoadPackVirtual (const char *dirname)
        pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
        pack->vpack = true;
        pack->ignorecase = false;
-       strlcpy (pack->filename, dirname, sizeof(pack->filename));
+       dp_strlcpy (pack->filename, dirname, sizeof(pack->filename));
        pack->handle = FILEDESC_INVALID;
        pack->numfiles = -1;
        pack->files = NULL;
@@ -1118,7 +1198,7 @@ FS_AddPack_Fullpath
  * plain directories.
  *
  */
-static qbool FS_AddPack_Fullpath(const char *pakfile, const char *shortname, qbool *already_loaded, qbool keep_plain_dirs)
+static qbool FS_AddPack_Fullpath(const char *pakfile, const char *shortname, qbool *already_loaded, qbool keep_plain_dirs, qbool dlcache)
 {
        searchpath_t *search;
        pack_t *pak = NULL;
@@ -1151,7 +1231,7 @@ static qbool FS_AddPack_Fullpath(const char *pakfile, const char *shortname, qbo
 
        if(pak)
        {
-               strlcpy(pak->shortname, shortname, sizeof(pak->shortname));
+               dp_strlcpy(pak->shortname, shortname, sizeof(pak->shortname));
 
                //Con_DPrintf("  Registered pack with short name %s\n", shortname);
                if(keep_plain_dirs)
@@ -1194,6 +1274,7 @@ static qbool FS_AddPack_Fullpath(const char *pakfile, const char *shortname, qbo
                        fs_searchpaths = search;
                }
                search->pack = pak;
+               search->pack->dlcache = dlcache;
                if(pak->vpack)
                {
                        dpsnprintf(search->filename, sizeof(search->filename), "%s/", pakfile);
@@ -1232,7 +1313,7 @@ FS_AddPack
  * If keep_plain_dirs is set, the pack will be added AFTER the first sequence of
  * plain directories.
  */
-qbool FS_AddPack(const char *pakfile, qbool *already_loaded, qbool keep_plain_dirs)
+qbool FS_AddPack(const char *pakfile, qbool *already_loaded, qbool keep_plain_dirs, qbool dlcache)
 {
        char fullpath[MAX_OSPATH];
        int index;
@@ -1242,7 +1323,7 @@ qbool FS_AddPack(const char *pakfile, qbool *already_loaded, qbool keep_plain_di
                *already_loaded = false;
 
        // then find the real name...
-       search = FS_FindFile(pakfile, &index, true);
+       search = FS_FindFile(pakfile, &index, NULL, true);
        if(!search || search->pack)
        {
                Con_Printf("could not find pak \"%s\"\n", pakfile);
@@ -1251,7 +1332,7 @@ qbool FS_AddPack(const char *pakfile, qbool *already_loaded, qbool keep_plain_di
 
        dpsnprintf(fullpath, sizeof(fullpath), "%s%s", search->filename, pakfile);
 
-       return FS_AddPack_Fullpath(fullpath, pakfile, already_loaded, keep_plain_dirs);
+       return FS_AddPack_Fullpath(fullpath, pakfile, already_loaded, keep_plain_dirs, dlcache);
 }
 
 
@@ -1269,7 +1350,7 @@ static void FS_AddGameDirectory (const char *dir)
        stringlist_t list;
        searchpath_t *search;
 
-       strlcpy (fs_gamedir, dir, sizeof (fs_gamedir));
+       dp_strlcpy (fs_gamedir, dir, sizeof (fs_gamedir));
 
        stringlistinit(&list);
        listdirectory(&list, "", dir);
@@ -1280,7 +1361,7 @@ static void FS_AddGameDirectory (const char *dir)
        {
                if (!strcasecmp(FS_FileExtension(list.strings[i]), "pak"))
                {
-                       FS_AddPack_Fullpath(list.strings[i], list.strings[i] + strlen(dir), NULL, false);
+                       FS_AddPack_Fullpath(list.strings[i], list.strings[i] + strlen(dir), NULL, false, false);
                }
        }
 
@@ -1290,7 +1371,7 @@ static void FS_AddGameDirectory (const char *dir)
                if (!strcasecmp(FS_FileExtension(list.strings[i]), "pk3") || !strcasecmp(FS_FileExtension(list.strings[i]), "obb") || !strcasecmp(FS_FileExtension(list.strings[i]), "pk3dir")
                        || !strcasecmp(FS_FileExtension(list.strings[i]), "dpk") || !strcasecmp(FS_FileExtension(list.strings[i]), "dpkdir"))
                {
-                       FS_AddPack_Fullpath(list.strings[i], list.strings[i] + strlen(dir), NULL, false);
+                       FS_AddPack_Fullpath(list.strings[i], list.strings[i] + strlen(dir), NULL, false, false);
                }
        }
 
@@ -1299,7 +1380,7 @@ static void FS_AddGameDirectory (const char *dir)
        // Add the directory to the search path
        // (unpacked files have the priority over packed files)
        search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
-       strlcpy (search->filename, dir, sizeof (search->filename));
+       dp_strlcpy (search->filename, dir, sizeof (search->filename));
        search->next = fs_searchpaths;
        fs_searchpaths = search;
 }
@@ -1399,6 +1480,49 @@ static void FS_ClearSearchPath (void)
        }
 }
 
+/*
+================
+FS_UnloadPacks_dlcache
+
+Like FS_ClearSearchPath() but unloads only the packs loaded from dlcache
+so we don't need to use a full FS_Rescan() to prevent
+content from the previous server and/or map from interfering with the next
+================
+*/
+void FS_UnloadPacks_dlcache(void)
+{
+       searchpath_t *search = fs_searchpaths, *searchprev = fs_searchpaths, *searchnext;
+
+       if (!fs_unload_dlcache.integer)
+               return;
+
+       while (search)
+       {
+               searchnext = search->next;
+               if (search->pack && search->pack->dlcache)
+               {
+                       Con_DPrintf("Unloading pack: %s\n", search->pack->shortname);
+
+                       // remove it from the search path list
+                       if (search == fs_searchpaths)
+                               fs_searchpaths = search->next;
+                       else
+                               searchprev->next = search->next;
+
+                       // close the file
+                       FILEDESC_CLOSE(search->pack->handle);
+                       // free any memory associated with it
+                       if (search->pack->files)
+                               Mem_Free(search->pack->files);
+                       Mem_Free(search->pack);
+                       Mem_Free(search);
+               }
+               else
+                       searchprev = search;
+               search = searchnext;
+       }
+}
+
 static void FS_AddSelfPack(void)
 {
        if(fs_selfpack)
@@ -1438,9 +1562,9 @@ void FS_Rescan (void)
        FS_AddGameHierarchy (gamedirname1);
        // update the com_modname (used for server info)
        if (gamedirname2 && gamedirname2[0])
-               strlcpy(com_modname, gamedirname2, sizeof(com_modname));
+               dp_strlcpy(com_modname, gamedirname2, sizeof(com_modname));
        else
-               strlcpy(com_modname, gamedirname1, sizeof(com_modname));
+               dp_strlcpy(com_modname, gamedirname1, sizeof(com_modname));
 
        // add the game-specific path, if any
        // (only used for mission packs and the like, which should set fs_modified)
@@ -1460,26 +1584,29 @@ void FS_Rescan (void)
                fs_modified = true;
                FS_AddGameHierarchy (fs_gamedirs[i]);
                // update the com_modname (used server info)
-               strlcpy (com_modname, fs_gamedirs[i], sizeof (com_modname));
+               dp_strlcpy (com_modname, fs_gamedirs[i], sizeof (com_modname));
                if(i)
-                       strlcat(gamedirbuf, va(vabuf, sizeof(vabuf), " %s", fs_gamedirs[i]), sizeof(gamedirbuf));
+                       dp_strlcat(gamedirbuf, va(vabuf, sizeof(vabuf), " %s", fs_gamedirs[i]), sizeof(gamedirbuf));
                else
-                       strlcpy(gamedirbuf, fs_gamedirs[i], sizeof(gamedirbuf));
+                       dp_strlcpy(gamedirbuf, fs_gamedirs[i], sizeof(gamedirbuf));
        }
        Cvar_SetQuick(&cvar_fs_gamedir, gamedirbuf); // so QC or console code can query it
 
        // add back the selfpack as new first item
        FS_AddSelfPack();
 
-       // set the default screenshot name to either the mod name or the
-       // gamemode screenshot name
-       if (strcmp(com_modname, gamedirname1))
-               Cvar_SetQuick (&scr_screenshot_name, com_modname);
-       else
-               Cvar_SetQuick (&scr_screenshot_name, gamescreenshotname);
-       
+       if (cls.state != ca_dedicated)
+       {
+               // set the default screenshot name to either the mod name or the
+               // gamemode screenshot name
+               if (strcmp(com_modname, gamedirname1))
+                       Cvar_SetQuick (&scr_screenshot_name, com_modname);
+               else
+                       Cvar_SetQuick (&scr_screenshot_name, gamescreenshotname);
+       }
+
        if((i = Sys_CheckParm("-modname")) && i < sys.argc - 1)
-               strlcpy(com_modname, sys.argv[i+1], sizeof(com_modname));
+               dp_strlcpy(com_modname, sys.argv[i+1], sizeof(com_modname));
 
        // If "-condebug" is in the command line, remove the previous log file
        if (Sys_CheckParm ("-condebug") != 0)
@@ -1571,7 +1698,7 @@ qbool FS_ChangeGameDirs(int numgamedirs, char gamedirs[][MAX_QPATH], qbool compl
 
        fs_numgamedirs = numgamedirs;
        for (i = 0;i < fs_numgamedirs;i++)
-               strlcpy(fs_gamedirs[i], gamedirs[i], sizeof(fs_gamedirs[i]));
+               dp_strlcpy(fs_gamedirs[i], gamedirs[i], sizeof(fs_gamedirs[i]));
 
        // reinitialize filesystem to detect the new paks
        FS_Rescan();
@@ -1585,8 +1712,8 @@ qbool FS_ChangeGameDirs(int numgamedirs, char gamedirs[][MAX_QPATH], qbool compl
        // unload all sounds so they will be reloaded from the new files as needed
        S_UnloadAllSounds_f(cmd_local);
 
-       // restart the video subsystem after the config is executed
-       Cbuf_InsertText(cmd_local, "\nloadconfig\nvid_restart\n\n");
+       // reset everything that can be and reload configs
+       Cbuf_InsertText(cmd_local, "\nloadconfig\n");
 
        return true;
 }
@@ -1619,7 +1746,7 @@ static void FS_GameDir_f(cmd_state_t *cmd)
        }
 
        for (i = 0;i < numgamedirs;i++)
-               strlcpy(gamedirs[i], Cmd_Argv(cmd, i+1), sizeof(gamedirs[i]));
+               dp_strlcpy(gamedirs[i], Cmd_Argv(cmd, i+1), sizeof(gamedirs[i]));
 
        if ((cls.state == ca_connected && !cls.demoplayback) || sv.active)
        {
@@ -1698,7 +1825,7 @@ const char *FS_CheckGameDir(const char *gamedir)
        ret = FS_SysCheckGameDir(va(vabuf, sizeof(vabuf), "%s%s/", fs_basedir, gamedir), buf, sizeof(buf));
        if(ret)
                return ret;
-       
+
        return fs_checkgamedir_missing;
 }
 
@@ -1731,7 +1858,7 @@ static void FS_ListGameDirs(void)
                        continue;
                if(!*info)
                        continue;
-               stringlistappend(&list2, list.strings[i]); 
+               stringlistappend(&list2, list.strings[i]);
        }
        stringlistfreecontents(&list);
 
@@ -1746,8 +1873,8 @@ static void FS_ListGameDirs(void)
                        continue;
                if(!*info)
                        continue;
-               strlcpy(fs_all_gamedirs[fs_all_gamedirs_count].name, list2.strings[i], sizeof(fs_all_gamedirs[fs_all_gamedirs_count].name));
-               strlcpy(fs_all_gamedirs[fs_all_gamedirs_count].description, info, sizeof(fs_all_gamedirs[fs_all_gamedirs_count].description));
+               dp_strlcpy(fs_all_gamedirs[fs_all_gamedirs_count].name, list2.strings[i], sizeof(fs_all_gamedirs[fs_all_gamedirs_count].name));
+               dp_strlcpy(fs_all_gamedirs[fs_all_gamedirs_count].description, info, sizeof(fs_all_gamedirs[fs_all_gamedirs_count].description));
                ++fs_all_gamedirs_count;
        }
 }
@@ -1778,7 +1905,7 @@ static void COM_InsertFlags(const char *buf) {
                if(i > args_left)
                        break;
                q = (char *)Mem_Alloc(fs_mempool, sz);
-               strlcpy(q, com_token, sz);
+               dp_strlcpy(q, com_token, sz);
                new_argv[i] = q;
                ++i;
        }
@@ -1801,19 +1928,21 @@ static int FS_ChooseUserDir(userdirmode_t userdirmode, char *userdir, size_t use
        {
                // fs_basedir is "" by default, to utilize this you can simply add your gamedir to the Resources in xcode
                // fs_userdir stores configurations to the Documents folder of the app
-               strlcpy(userdir, "../Documents/", MAX_OSPATH);
+               dp_strlcpy(userdir, "../Documents/", MAX_OSPATH);
                return 1;
        }
        return -1;
 
 #elif defined(WIN32)
-       char *homedir;
+       char homedir[WSTRBUF];
+       wchar *homedirw;
 #if _MSC_VER >= 1400
-       size_t homedirlen;
+       size_t homedirwlen;
 #endif
-       TCHAR mydocsdir[MAX_PATH + 1];
+       wchar_t mydocsdirw[WSTRBUF];
+       char mydocsdir[WSTRBUF];
        wchar_t *savedgamesdirw;
-       char savedgamesdir[MAX_OSPATH];
+       char savedgamesdir[WSTRBUF] = {0};
        int fd;
        char vabuf[1024];
 
@@ -1823,28 +1952,31 @@ static int FS_ChooseUserDir(userdirmode_t userdirmode, char *userdir, size_t use
        default:
                return -1;
        case USERDIRMODE_NOHOME:
-               strlcpy(userdir, fs_basedir, userdirsize);
+               dp_strlcpy(userdir, fs_basedir, userdirsize);
                break;
        case USERDIRMODE_MYGAMES:
                if (!shfolder_dll)
                        Sys_LoadDependency(shfolderdllnames, &shfolder_dll, shfolderfuncs);
                mydocsdir[0] = 0;
-               if (qSHGetFolderPath && qSHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, mydocsdir) == S_OK)
+               if (qSHGetFolderPath && qSHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, mydocsdirw) == S_OK)
                {
+                       narrow(mydocsdirw, mydocsdir);
                        dpsnprintf(userdir, userdirsize, "%s/My Games/%s/", mydocsdir, gameuserdirname);
                        break;
                }
 #if _MSC_VER >= 1400
-               _dupenv_s(&homedir, &homedirlen, "USERPROFILE");
-               if(homedir)
+               _wdupenv_s(&homedirw, &homedirwlen, L"USERPROFILE");
+               narrow(homedirw, homedir);
+               if(homedir[0])
                {
                        dpsnprintf(userdir, userdirsize, "%s/.%s/", homedir, gameuserdirname);
-                       free(homedir);
+                       free(homedirw);
                        break;
                }
 #else
-               homedir = getenv("USERPROFILE");
-               if(homedir)
+               homedirw = _wgetenv(L"USERPROFILE");
+               narrow(homedirw, homedir);
+               if(homedir[0])
                {
                        dpsnprintf(userdir, userdirsize, "%s/.%s/", homedir, gameuserdirname);
                        break;
@@ -1869,12 +2001,7 @@ static int FS_ChooseUserDir(userdirmode_t userdirmode, char *userdir, size_t use
 */
                        if (qSHGetKnownFolderPath(&qFOLDERID_SavedGames, qKF_FLAG_CREATE | qKF_FLAG_NO_ALIAS, NULL, &savedgamesdirw) == S_OK)
                        {
-                               memset(savedgamesdir, 0, sizeof(savedgamesdir));
-#if _MSC_VER >= 1400
-                               wcstombs_s(NULL, savedgamesdir, sizeof(savedgamesdir), savedgamesdirw, sizeof(savedgamesdir)-1);
-#else
-                               wcstombs(savedgamesdir, savedgamesdirw, sizeof(savedgamesdir)-1);
-#endif
+                               narrow(savedgamesdirw, savedgamesdir);
                                qCoTaskMemFree(savedgamesdirw);
                        }
                        qCoUninitialize();
@@ -1896,7 +2023,7 @@ static int FS_ChooseUserDir(userdirmode_t userdirmode, char *userdir, size_t use
        default:
                return -1;
        case USERDIRMODE_NOHOME:
-               strlcpy(userdir, fs_basedir, userdirsize);
+               dp_strlcpy(userdir, fs_basedir, userdirsize);
                break;
        case USERDIRMODE_HOME:
                homedir = getenv("HOME");
@@ -1969,9 +2096,10 @@ void FS_Init_Commands(void)
 {
        Cvar_RegisterVariable (&scr_screenshot_name);
        Cvar_RegisterVariable (&fs_empty_files_in_pack_mark_deletions);
+       Cvar_RegisterVariable (&fs_unload_dlcache);
        Cvar_RegisterVariable (&cvar_fs_gamedir);
 
-       Cmd_AddCommand(CF_SHARED, "gamedir", FS_GameDir_f, "changes active gamedir list (can take multiple arguments), not including base directory (example usage: gamedir ctf)");
+       Cmd_AddCommand(CF_SHARED, "gamedir", FS_GameDir_f, "changes active gamedir list, can take multiple arguments which shouldn't include the base directory, the last gamedir is the \"primary\" and files will be saved there (example usage: gamedir ctf id1)");
        Cmd_AddCommand(CF_SHARED, "fs_rescan", FS_Rescan_f, "rescans filesystem for new pack archives and any other changes");
        Cmd_AddCommand(CF_SHARED, "path", FS_Path_f, "print searchpath (game directories and archives)");
        Cmd_AddCommand(CF_SHARED, "dir", FS_Dir_f, "list files in searchpath matching an * filename pattern, one per line");
@@ -1994,7 +2122,7 @@ static void FS_Init_Dir (void)
        i = Sys_CheckParm ("-basedir");
        if (i && i < sys.argc-1)
        {
-               strlcpy (fs_basedir, sys.argv[i+1], sizeof (fs_basedir));
+               dp_strlcpy (fs_basedir, sys.argv[i+1], sizeof (fs_basedir));
                i = (int)strlen (fs_basedir);
                if (i > 0 && (fs_basedir[i-1] == '\\' || fs_basedir[i-1] == '/'))
                        fs_basedir[i-1] = 0;
@@ -2003,7 +2131,7 @@ static void FS_Init_Dir (void)
        {
 // If the base directory is explicitly defined by the compilation process
 #ifdef DP_FS_BASEDIR
-               strlcpy(fs_basedir, DP_FS_BASEDIR, sizeof(fs_basedir));
+               dp_strlcpy(fs_basedir, DP_FS_BASEDIR, sizeof(fs_basedir));
 #elif defined(__ANDROID__)
                dpsnprintf(fs_basedir, sizeof(fs_basedir), "/sdcard/%s/", gameuserdirname);
 #elif defined(MACOSX)
@@ -2011,7 +2139,7 @@ static void FS_Init_Dir (void)
                if (strstr(sys.argv[0], ".app/"))
                {
                        char *split;
-                       strlcpy(fs_basedir, sys.argv[0], sizeof(fs_basedir));
+                       dp_strlcpy(fs_basedir, sys.argv[0], sizeof(fs_basedir));
                        split = strstr(fs_basedir, ".app/");
                        if (split)
                        {
@@ -2023,7 +2151,7 @@ static void FS_Init_Dir (void)
                                if (stat(va(vabuf, sizeof(vabuf), "%s/Contents/Resources/%s", fs_basedir, gamedirname1), &statresult) == 0)
                                {
                                        // found gamedir inside Resources, use it
-                                       strlcat(fs_basedir, "Contents/Resources/", sizeof(fs_basedir));
+                                       dp_strlcat(fs_basedir, "Contents/Resources/", sizeof(fs_basedir));
                                }
                                else
                                {
@@ -2042,7 +2170,7 @@ static void FS_Init_Dir (void)
        memset(fs_basedir + sizeof(fs_basedir) - 2, 0, 2);
        // add a path separator to the end of the basedir if it lacks one
        if (fs_basedir[0] && fs_basedir[strlen(fs_basedir) - 1] != '/' && fs_basedir[strlen(fs_basedir) - 1] != '\\')
-               strlcat(fs_basedir, "/", sizeof(fs_basedir));
+               dp_strlcat(fs_basedir, "/", sizeof(fs_basedir));
 
        // Add the personal game directory
        if((i = Sys_CheckParm("-userdir")) && i < sys.argc - 1)
@@ -2052,7 +2180,7 @@ static void FS_Init_Dir (void)
        else
        {
 #ifdef DP_FS_USERDIR
-               strlcpy(fs_userdir, DP_FS_USERDIR, sizeof(fs_userdir));
+               dp_strlcpy(fs_userdir, DP_FS_USERDIR, sizeof(fs_userdir));
 #else
                int dirmode;
                int highestuserdirmode = USERDIRMODE_COUNT - 1;
@@ -2130,7 +2258,7 @@ static void FS_Init_Dir (void)
                        if(p == fs_checkgamedir_missing)
                                Con_Printf(CON_WARN "WARNING: -game %s%s/ not found!\n", fs_basedir, sys.argv[i]);
                        // add the gamedir to the list of active gamedirs
-                       strlcpy (fs_gamedirs[fs_numgamedirs], sys.argv[i], sizeof(fs_gamedirs[fs_numgamedirs]));
+                       dp_strlcpy (fs_gamedirs[fs_numgamedirs], sys.argv[i], sizeof(fs_gamedirs[fs_numgamedirs]));
                        fs_numgamedirs++;
                }
        }
@@ -2237,6 +2365,9 @@ static filedesc_t FS_SysOpenFiledesc(const char *filepath, const char *mode, qbo
        int mod, opt;
        unsigned int ind;
        qbool dolock = false;
+       #ifdef WIN32
+       wchar filepathw[WSTRBUF] = {0};
+       #endif
 
        // Parse the mode string
        switch (mode[0])
@@ -2288,10 +2419,11 @@ static filedesc_t FS_SysOpenFiledesc(const char *filepath, const char *mode, qbo
        handle = SDL_RWFromFile(filepath, mode);
 #else
 # ifdef WIN32
+       widen(filepath, filepathw);
 #  if _MSC_VER >= 1400
-       _sopen_s(&handle, filepath, mod | opt, (dolock ? ((mod == O_RDONLY) ? _SH_DENYRD : _SH_DENYRW) : _SH_DENYNO), _S_IREAD | _S_IWRITE);
+       _wsopen_s(&handle, filepathw, mod | opt, (dolock ? ((mod == O_RDONLY) ? _SH_DENYRD : _SH_DENYRW) : _SH_DENYNO), _S_IREAD | _S_IWRITE);
 #  else
-       handle = _sopen (filepath, mod | opt, (dolock ? ((mod == O_RDONLY) ? _SH_DENYRD : _SH_DENYRW) : _SH_DENYNO), _S_IREAD | _S_IWRITE);
+       handle = _wsopen (filepathw, mod | opt, (dolock ? ((mod == O_RDONLY) ? _SH_DENYRD : _SH_DENYRW) : _SH_DENYNO), _S_IREAD | _S_IWRITE);
 #  endif
 # else
        handle = open (filepath, mod | opt, 0666);
@@ -2515,7 +2647,7 @@ int FS_CheckNastyPath (const char *path, qbool isgamedir)
 ====================
 FS_SanitizePath
 
-Sanitize path (replace non-portable characters 
+Sanitize path (replace non-portable characters
 with portable ones in-place, etc)
 ====================
 */
@@ -2536,7 +2668,7 @@ Return the searchpath where the file was found (or NULL)
 and the file index in the package if relevant
 ====================
 */
-static searchpath_t *FS_FindFile (const char *name, int* index, qbool quiet)
+static searchpath_t *FS_FindFile (const char *name, int *index, const char **canonicalname, qbool quiet)
 {
        searchpath_t *search;
        pack_t *pak;
@@ -2574,15 +2706,18 @@ static searchpath_t *FS_FindFile (const char *name, int* index, qbool quiet)
 
                                                if (index != NULL)
                                                        *index = -1;
+                                               if (canonicalname)
+                                                       *canonicalname = NULL;
                                                return NULL;
                                        }
 
                                        if (!quiet && developer_extra.integer)
-                                               Con_DPrintf("FS_FindFile: %s in %s\n",
-                                                                       pak->files[middle].name, pak->filename);
+                                               Con_DPrintf("FS_FindFile: %s in %s\n", pak->files[middle].name, pak->filename);
 
                                        if (index != NULL)
                                                *index = middle;
+                                       if (canonicalname)
+                                               *canonicalname = pak->files[middle].name;
                                        return search;
                                }
 
@@ -2604,6 +2739,8 @@ static searchpath_t *FS_FindFile (const char *name, int* index, qbool quiet)
 
                                if (index != NULL)
                                        *index = -1;
+                               if (canonicalname)
+                                       *canonicalname = name;
                                return search;
                        }
                }
@@ -2614,6 +2751,8 @@ static searchpath_t *FS_FindFile (const char *name, int* index, qbool quiet)
 
        if (index != NULL)
                *index = -1;
+       if (canonicalname)
+               *canonicalname = NULL;
        return NULL;
 }
 
@@ -2630,7 +2769,7 @@ static qfile_t *FS_OpenReadFile (const char *filename, qbool quiet, qbool nonblo
        searchpath_t *search;
        int pack_ind;
 
-       search = FS_FindFile (filename, &pack_ind, quiet);
+       search = FS_FindFile (filename, &pack_ind, NULL, quiet);
 
        // Not found?
        if (search == NULL)
@@ -2672,7 +2811,7 @@ static qfile_t *FS_OpenReadFile (const char *filename, qbool quiet, qbool nonblo
                        if(count < 0)
                                return NULL;
                        linkbuf[count] = 0;
-                       
+
                        // Now combine the paths...
                        mergeslash = strrchr(filename, '/');
                        mergestart = linkbuf;
@@ -3495,7 +3634,7 @@ void FS_DefaultExtension (char *path, const char *extension, size_t size_path)
                src--;
        }
 
-       strlcat (path, extension, size_path);
+       dp_strlcat (path, extension, size_path);
 }
 
 
@@ -3511,7 +3650,7 @@ int FS_FileType (const char *filename)
        searchpath_t *search;
        char fullpath[MAX_OSPATH];
 
-       search = FS_FindFile (filename, NULL, true);
+       search = FS_FindFile (filename, NULL, NULL, true);
        if(!search)
                return FS_FILETYPE_NONE;
 
@@ -3528,11 +3667,15 @@ int FS_FileType (const char *filename)
 FS_FileExists
 
 Look for a file in the packages and in the filesystem
+Returns its canonical name (VFS path with correct capitalisation) if found, else NULL.
+If the file is found outside a pak, this will be the same pointer as passed in.
 ==================
 */
-qbool FS_FileExists (const char *filename)
+const char *FS_FileExists (const char *filename)
 {
-       return (FS_FindFile (filename, NULL, true) != NULL);
+       const char *canonicalname;
+
+       return FS_FindFile(filename, NULL, &canonicalname, true) ? canonicalname : NULL;
 }
 
 
@@ -3550,8 +3693,10 @@ int FS_SysFileType (const char *path)
 # ifndef INVALID_FILE_ATTRIBUTES
 #  define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
 # endif
-
-       DWORD result = GetFileAttributes(path);
+       wchar pathw[WSTRBUF] = {0};
+       DWORD result;
+       widen(path, pathw);
+       result = GetFileAttributesW(pathw);
 
        if(result == INVALID_FILE_ATTRIBUTES)
                return FS_FILETYPE_NONE;
@@ -3639,7 +3784,7 @@ fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet, const
                        for (i = 0;i < pak->numfiles;i++)
                        {
                                char temp[MAX_OSPATH];
-                               strlcpy(temp, pak->files[i].name, sizeof(temp));
+                               dp_strlcpy(temp, pak->files[i].name, sizeof(temp));
                                while (temp[0])
                                {
                                        if (matchpattern(temp, (char *)pattern, true))
@@ -3711,7 +3856,7 @@ fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet, const
 
                                // prevseparator points past the '/' right before the wildcard and nextseparator at the one following it (or at the end of the string)
                                // copy everything up except nextseperator
-                               strlcpy(subpattern, pattern, min(sizeof(subpattern), (size_t) (nextseparator - pattern + 1)));
+                               dp_ustr2stp(subpattern, sizeof(subpattern), pattern, nextseparator - pattern);
                                // find the last '/' before the wildcard
                                prevseparator = strrchr( subpattern, '/' );
                                if (!prevseparator)
@@ -3720,13 +3865,13 @@ fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet, const
                                        prevseparator++;
                                // copy everything from start to the previous including the '/' (before the wildcard)
                                // everything up to start is already included in the path of matchedSet's entries
-                               strlcpy(subpath, start, min(sizeof(subpath), (size_t) ((prevseparator - subpattern) - (start - pattern) + 1)));
+                               dp_ustr2stp(subpath, sizeof(subpath), start, (prevseparator - subpattern) - (start - pattern));
 
                                // for each entry in matchedSet try to open the subdirectories specified in subpath
                                for( dirlistindex = 0 ; dirlistindex < matchedSet.numstrings ; dirlistindex++ ) {
                                        char temp[MAX_OSPATH];
-                                       strlcpy( temp, matchedSet.strings[ dirlistindex ], sizeof(temp) );
-                                       strlcat( temp, subpath, sizeof(temp) );
+                                       dp_strlcpy( temp, matchedSet.strings[ dirlistindex ], sizeof(temp) );
+                                       dp_strlcat( temp, subpath, sizeof(temp) );
                                        listdirectory( &foundSet, searchpath->filename, temp );
                                }
                                if( dirlistindex == 0 ) {
@@ -3904,9 +4049,9 @@ void FS_Which_f(cmd_state_t *cmd)
        {
                Con_Printf("usage:\n%s <file>\n", Cmd_Argv(cmd, 0));
                return;
-       }  
+       }
        filename = Cmd_Argv(cmd, 1);
-       sp = FS_FindFile(filename, &index, true);
+       sp = FS_FindFile(filename, &index, NULL, true);
        if (!sp) {
                Con_Printf("%s isn't anywhere\n", filename);
                return;
@@ -3926,7 +4071,7 @@ void FS_Which_f(cmd_state_t *cmd)
 const char *FS_WhichPack(const char *filename)
 {
        int index;
-       searchpath_t *sp = FS_FindFile(filename, &index, true);
+       searchpath_t *sp = FS_FindFile(filename, &index, NULL, true);
        if(sp && sp->pack)
                return sp->pack->shortname;
        else if(sp)
@@ -4057,7 +4202,7 @@ unsigned char *FS_Deflate(const unsigned char *data, size_t size, size_t *deflat
                Mem_Free(tmp);
                return NULL;
        }
-       
+
        if(qz_deflateEnd(&strm) != Z_OK)
        {
                Con_Printf("FS_Deflate: deflateEnd failed\n");
@@ -4084,7 +4229,7 @@ unsigned char *FS_Deflate(const unsigned char *data, size_t size, size_t *deflat
 
        memcpy(out, tmp, strm.total_out);
        Mem_Free(tmp);
-       
+
        return out;
 }
 
@@ -4151,7 +4296,7 @@ unsigned char *FS_Inflate(const unsigned char *data, size_t size, size_t *inflat
                        case Z_STREAM_END:
                        case Z_OK:
                                break;
-                               
+
                        case Z_STREAM_ERROR:
                                Con_Print("FS_Inflate: stream error!\n");
                                break;
@@ -4167,7 +4312,7 @@ unsigned char *FS_Inflate(const unsigned char *data, size_t size, size_t *inflat
                        default:
                                Con_Print("FS_Inflate: unknown error!\n");
                                break;
-                               
+
                }
                if(ret != Z_OK && ret != Z_STREAM_END)
                {
@@ -4195,6 +4340,6 @@ unsigned char *FS_Inflate(const unsigned char *data, size_t size, size_t *inflat
        Mem_Free(outbuf.data);
 
        *inflated_size = (size_t)outbuf.cursize;
-       
+
        return out;
 }
diff --git a/fs.h b/fs.h
index b8ada8b990b831b04c55441845e96958da262d59..d839833172cbadd9d58ea3e17abee5b458f0477c 100644 (file)
--- a/fs.h
+++ b/fs.h
@@ -61,7 +61,7 @@ typedef struct vfs_s
 // IMPORTANT: the file path is automatically prefixed by the current game directory for
 // each file created by FS_WriteFile, or opened in "write" or "append" mode by FS_OpenRealFile
 
-qbool FS_AddPack(const char *pakfile, qbool *already_loaded, qbool keep_plain_dirs); // already_loaded may be NULL if caller does not care
+qbool FS_AddPack(const char *pakfile, qbool *already_loaded, qbool keep_plain_dirs, qbool dlcache); // already_loaded may be NULL if caller does not care
 const char *FS_WhichPack(const char *filename);
 void FS_CreatePath (char *path);
 int FS_SysOpenFD(const char *filepath, const char *mode, qbool nonblocking); // uses absolute path
@@ -102,6 +102,7 @@ extern int fs_all_gamedirs_count;
 qbool FS_ChangeGameDirs(int numgamedirs, char gamedirs[][MAX_QPATH], qbool complain, qbool failmissing);
 qbool FS_IsRegisteredQuakePack(const char *name);
 int FS_CRCFile(const char *filename, size_t *filesizepointer);
+void FS_UnloadPacks_dlcache(void);
 void FS_Rescan(void);
 
 typedef struct fssearch_s
@@ -130,11 +131,17 @@ void FS_DefaultExtension (char *path, const char *extension, size_t size_path);
 #define FS_FILETYPE_NONE 0
 #define FS_FILETYPE_FILE 1
 #define FS_FILETYPE_DIRECTORY 2
-int FS_FileType (const char *filename);                // the file can be into a package
-int FS_SysFileType (const char *filename);             // only look for files outside of packages
-
-qbool FS_FileExists (const char *filename);            // the file can be into a package
-qbool FS_SysFileExists (const char *filename); // only look for files outside of packages
+/// Look for a file in the packages and in the filesystem
+int FS_FileType (const char *filename);
+/// Look for a file in the filesystem only
+int FS_SysFileType (const char *filename);
+
+/// Look for a file in the packages and in the filesystem
+/// Returns its canonical name (same case as used in the pack) if found, else NULL.
+/// If the file is found outside a pak, this will be the same pointer as passed in.
+const char *FS_FileExists (const char *filename);
+/// Look for a file in the filesystem only
+qbool FS_SysFileExists (const char *filename);
 
 unsigned char *FS_Deflate(const unsigned char *data, size_t size, size_t *deflated_size, int level, mempool_t *mempool);
 unsigned char *FS_Inflate(const unsigned char *data, size_t size, size_t *inflated_size, mempool_t *mempool);
diff --git a/ft2.c b/ft2.c
index b7421b218c3f79abe5ebd11039c51510ea868ade..64efe9a674c93a4834458e55fb7c96710b26673c 100644 (file)
--- a/ft2.c
+++ b/ft2.c
@@ -27,6 +27,25 @@ static int img_fontmap[256] = {
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
 };
 
+/*
+ * Some big blocks of Unicode characters, according to
+ *     http://www.unicode.org/Public/UNIDATA/Blocks.txt
+ *
+ * Let's call these "bigblocks".
+ * These characters are "spreaded", and ordinary maps will 
+ *     waste huge amount of resources rendering/caching unused glyphs.
+ *
+ * So, a new design is introduced to solve the problem:
+ *     incremental maps, in which only used glyphs are stored.
+ */
+static const Uchar unicode_bigblocks[] = {
+       0x3400, 0x4DBF,         //   6592  CJK Unified Ideographs Extension A
+       0x4E00, 0x9FFF,         //  20992  CJK Unified Ideographs
+       0xAC00, 0xD7AF,         //  11184  Hangul Syllables
+       0xE000, 0xF8FF,         //   6400  Private Use Area
+       0x10000, 0x10FFFF   //         Everything above
+};
+
 /*
 ================================================================================
 CVars introduced with the freetype extension
@@ -34,14 +53,15 @@ CVars introduced with the freetype extension
 */
 
 cvar_t r_font_disable_freetype = {CF_CLIENT | CF_ARCHIVE, "r_font_disable_freetype", "0", "disable freetype support for fonts entirely"};
-cvar_t r_font_use_alpha_textures = {CF_CLIENT | CF_ARCHIVE, "r_font_use_alpha_textures", "0", "use alpha-textures for font rendering, this should safe memory"};
 cvar_t r_font_size_snapping = {CF_CLIENT | CF_ARCHIVE, "r_font_size_snapping", "1", "stick to good looking font sizes whenever possible - bad when the mod doesn't support it!"};
 cvar_t r_font_kerning = {CF_CLIENT | CF_ARCHIVE, "r_font_kerning", "1", "Use kerning if available"};
-cvar_t r_font_diskcache = {CF_CLIENT | CF_ARCHIVE, "r_font_diskcache", "0", "save font textures to disk for future loading rather than generating them every time"};
+cvar_t r_font_diskcache = {CF_CLIENT | CF_ARCHIVE, "r_font_diskcache", "0", "[deprecated, not effective] save font textures to disk for future loading rather than generating them every time"};
 cvar_t r_font_compress = {CF_CLIENT | CF_ARCHIVE, "r_font_compress", "0", "use texture compression on font textures to save video memory"};
 cvar_t r_font_nonpoweroftwo = {CF_CLIENT | CF_ARCHIVE, "r_font_nonpoweroftwo", "1", "use nonpoweroftwo textures for font (saves memory, potentially slower)"};
 cvar_t developer_font = {CF_CLIENT | CF_ARCHIVE, "developer_font", "0", "prints debug messages about fonts"};
 
+cvar_t r_font_disable_incmaps = {CF_CLIENT | CF_ARCHIVE, "r_font_disable_incmaps", "0", "always to load a full glyph map for individual unmapped character, even when it will mean extreme resources waste"};
+
 #ifndef DP_FREETYPE_STATIC
 
 /*
@@ -268,7 +288,7 @@ static const unsigned char *fontfilecache_LoadFile(const char *path, qbool quiet
                for(i = 0; i < MAX_FONTFILES; ++i)
                        if(fontfiles[i].refcount <= 0)
                        {
-                               strlcpy(fontfiles[i].path, path, sizeof(fontfiles[i].path));
+                               dp_strlcpy(fontfiles[i].path, path, sizeof(fontfiles[i].path));
                                fontfiles[i].len = *filesizepointer;
                                fontfiles[i].buf = buf;
                                fontfiles[i].refcount = 1;
@@ -424,13 +444,14 @@ void Font_Init(void)
 {
        Cvar_RegisterVariable(&r_font_nonpoweroftwo);
        Cvar_RegisterVariable(&r_font_disable_freetype);
-       Cvar_RegisterVariable(&r_font_use_alpha_textures);
        Cvar_RegisterVariable(&r_font_size_snapping);
        Cvar_RegisterVariable(&r_font_kerning);
        Cvar_RegisterVariable(&r_font_diskcache);
        Cvar_RegisterVariable(&r_font_compress);
        Cvar_RegisterVariable(&developer_font);
 
+       Cvar_RegisterVariable(&r_font_disable_incmaps);
+
        // let's open it at startup already
        Font_OpenLibrary();
 }
@@ -526,7 +547,7 @@ qbool Font_LoadFont(const char *name, dp_font_t *dpfnt)
                        Mem_Free(ft2);
                        return false;
                }
-               strlcpy(ft2->name, name, sizeof(ft2->name));
+               dp_strlcpy(ft2->name, name, sizeof(ft2->name));
                ft2->image_font = true;
                ft2->has_kerning = false;
        }
@@ -703,7 +724,7 @@ static qbool Font_LoadFile(const char *name, int _face, ft2_settings_t *settings
                        Con_Printf(CON_ERROR "Failed to add attachment %u to %s\n", (unsigned)i, font->name);
        }
 
-       strlcpy(font->name, name, sizeof(font->name));
+       dp_strlcpy(font->name, name, sizeof(font->name));
        font->image_font = false;
        font->has_kerning = !!(((FT_Face)(font->face))->face_flags & FT_FACE_FLAG_KERNING);
        return true;
@@ -886,7 +907,7 @@ static void Font_Postprocess(ft2_font_t *fnt, unsigned char *imagedata, int pitc
 }
 
 static float Font_SearchSize(ft2_font_t *font, FT_Face fontface, float size);
-static qbool Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch, ft2_font_map_t **outmap);
+static qbool Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch, ft2_font_map_t **outmap, int *outmapch, qbool incmap_ok);
 static qbool Font_LoadSize(ft2_font_t *font, float size, qbool check_only)
 {
        int map_index;
@@ -933,7 +954,7 @@ static qbool Font_LoadSize(ft2_font_t *font, float size, qbool check_only)
        temp.sfx = (1.0/64.0)/(double)size;
        temp.sfy = (1.0/64.0)/(double)size;
        temp.intSize = -1; // negative value: LoadMap must search now :)
-       if (!Font_LoadMap(font, &temp, 0, &fmap))
+       if (!Font_LoadMap(font, &temp, 0, &fmap, NULL, false))
        {
                Con_Printf(CON_ERROR "ERROR: can't load the first character map for %s\n"
                           "This is fatal\n",
@@ -951,6 +972,7 @@ static qbool Font_LoadSize(ft2_font_t *font, float size, qbool check_only)
        {
                Uchar l, r;
                FT_Vector kernvec;
+               fmap->kerning = (ft2_kerning_t *)Mem_Alloc(font_mempool, sizeof(ft2_kerning_t));
                for (l = 0; l < 256; ++l)
                {
                        for (r = 0; r < 256; ++r)
@@ -960,13 +982,13 @@ static qbool Font_LoadSize(ft2_font_t *font, float size, qbool check_only)
                                ur = qFT_Get_Char_Index((FT_Face)font->face, r);
                                if (qFT_Get_Kerning((FT_Face)font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
                                {
-                                       fmap->kerning.kerning[l][r][0] = 0;
-                                       fmap->kerning.kerning[l][r][1] = 0;
+                                       fmap->kerning->kerning[l][r][0] = 0;
+                                       fmap->kerning->kerning[l][r][1] = 0;
                                }
                                else
                                {
-                                       fmap->kerning.kerning[l][r][0] = Font_SnapTo((kernvec.x / 64.0) / fmap->size, 1 / fmap->size);
-                                       fmap->kerning.kerning[l][r][1] = Font_SnapTo((kernvec.y / 64.0) / fmap->size, 1 / fmap->size);
+                                       fmap->kerning->kerning[l][r][0] = Font_SnapTo((kernvec.x / 64.0) / fmap->size, 1 / fmap->size);
+                                       fmap->kerning->kerning[l][r][1] = Font_SnapTo((kernvec.y / 64.0) / fmap->size, 1 / fmap->size);
                                }
                        }
                }
@@ -1074,8 +1096,8 @@ qbool Font_GetKerningForMap(ft2_font_t *font, int map_index, float w, float h, U
        {
                //Con_Printf("%g : %f, %f, %f :: %f\n", (w / (float)fmap->size), w, fmap->size, fmap->intSize, Font_VirtualToRealSize(w));
                // quick-kerning, be aware of the size: scale it
-               if (outx) *outx = fmap->kerning.kerning[left][right][0];// * (w / (float)fmap->size);
-               if (outy) *outy = fmap->kerning.kerning[left][right][1];// * (h / (float)fmap->size);
+               if (outx) *outx = fmap->kerning->kerning[left][right][0];// * (w / (float)fmap->size);
+               if (outy) *outy = fmap->kerning->kerning[left][right][1];// * (h / (float)fmap->size);
                return true;
        }
        else
@@ -1123,16 +1145,40 @@ qbool Font_GetKerningForSize(ft2_font_t *font, float w, float h, Uchar left, Uch
        return Font_GetKerningForMap(font, Font_IndexForSize(font, h, NULL, NULL), w, h, left, right, outx, outy);
 }
 
-static void UnloadMapRec(ft2_font_map_t *map)
+// this is used to gracefully unload a map chain; the passed map
+// needs not necessarily be a startmap, so maps ahead of it can be kept
+static void UnloadMapChain(ft2_font_map_t *map)
 {
-       if (map->pic)
+       int i;
+       ft2_font_map_t *nextmap;
+       // these may only be in a startmap
+       if (map->kerning != NULL)
+               Mem_Free(map->kerning);
+       if (map->incmap != NULL)
+       {
+               for (i = 0; i < FONT_CHARS_PER_LINE; ++i)
+                       if (map->incmap->data_tier1[i] != NULL)
+                               Mem_Free(map->incmap->data_tier1[i]);
+                       else
+                               break;
+               for (i = 0; i < FONT_CHAR_LINES; ++i)
+                       if (map->incmap->data_tier2[i] != NULL)
+                               Mem_Free(map->incmap->data_tier2[i]);
+                       else
+                               break;
+               Mem_Free(map->incmap);
+       }
+       while (map != NULL)
        {
-               //Draw_FreePic(map->pic); // FIXME: refcounting needed...
-               map->pic = NULL;
+               if (map->pic)
+               {
+                       //Draw_FreePic(map->pic); // FIXME: refcounting needed...
+                       map->pic = NULL;
+               }
+               nextmap = map->next;
+               Mem_Free(map);
+               map = nextmap;
        }
-       if (map->next)
-               UnloadMapRec(map->next);
-       Mem_Free(map);
 }
 
 void Font_UnloadFont(ft2_font_t *font)
@@ -1157,7 +1203,7 @@ void Font_UnloadFont(ft2_font_t *font)
        {
                if (font->font_maps[i])
                {
-                       UnloadMapRec(font->font_maps[i]);
+                       UnloadMapChain(font->font_maps[i]);
                        font->font_maps[i] = NULL;
                }
        }
@@ -1200,33 +1246,179 @@ static float Font_SearchSize(ft2_font_t *font, FT_Face fontface, float size)
        }
 }
 
-static qbool Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch, ft2_font_map_t **outmap)
+// helper inline functions for incmap_post_process:
+
+static inline void update_pic_for_fontmap(ft2_font_map_t *fontmap, const char *identifier,
+               int width, int height, unsigned char *data)
+{
+       fontmap->pic = Draw_NewPic(identifier, width, height, data, TEXTYPE_RGBA,
+               TEXF_ALPHA | TEXF_CLAMP | (r_font_compress.integer > 0 ? TEXF_COMPRESS : 0));
+}
+
+// glyphs' texture coords needs to be fixed when merging to bigger texture
+static inline void transform_glyph_coords(glyph_slot_t *glyph, float shiftx, float shifty, float scalex, float scaley)
 {
+       glyph->txmin = glyph->txmin * scalex + shiftx;
+       glyph->txmax = glyph->txmax * scalex + shiftx;
+       glyph->tymin = glyph->tymin * scaley + shifty;
+       glyph->tymax = glyph->tymax * scaley + shifty;
+}
+#define fix_glyph_coords_tier1(glyph, order) transform_glyph_coords(glyph, order / (float)FONT_CHARS_PER_LINE, 0.0f, 1.0f / (float)FONT_CHARS_PER_LINE, 1.0f)
+#define fix_glyph_coords_tier2(glyph, order) transform_glyph_coords(glyph, 0.0f, order / (float)FONT_CHARS_PER_LINE, 1.0f, 1.0f / (float)FONT_CHARS_PER_LINE)
+
+// pull glyph things from sourcemap to targetmap
+static inline void merge_single_map(ft2_font_map_t *targetmap, int targetindex, ft2_font_map_t *sourcemap, int sourceindex)
+{
+       targetmap->glyphs[targetindex] = sourcemap->glyphs[sourceindex];
+       targetmap->glyphchars[targetindex] = sourcemap->glyphchars[sourceindex];
+}
+
+#define calc_data_arguments(w, h)                      \
+               width = startmap->glyphSize * w;        \
+               height = startmap->glyphSize * h;       \
+               pitch = width * bytes_per_pixel;        \
+               datasize = height * pitch;
+
+// do incremental map process
+static inline void incmap_post_process(font_incmap_t *incmap, Uchar ch,
+               unsigned char *data, ft2_font_map_t **outmap, int *outmapch)
+{
+       #define bytes_per_pixel 4
+
+       int index, targetmap_at;
+       // where will the next `data` be placed
+       int tier1_data_index, tier2_data_index;
+       // metrics of data to manipulate
+       int width, height, pitch, datasize;
+       int i, j, x, y;
+       unsigned char *newdata, *chunk;
+       ft2_font_map_t *startmap, *targetmap, *currentmap;
+       #define M FONT_CHARS_PER_LINE
+       #define N FONT_CHAR_LINES
+
+       startmap = incmap->fontmap;
+       index = incmap->charcount;
+       tier1_data_index = index % M;
+       tier2_data_index = incmap->tier1_merged;
+
+       incmap->data_tier1[tier1_data_index] = data;
+
+       if (index % M == M - 1)
+       {
+               // tier 1 reduction, pieces to line
+               calc_data_arguments(1, 1);
+               targetmap_at = incmap->tier2_merged + incmap->tier1_merged;
+               targetmap = startmap;
+               for (i = 0; i < targetmap_at; ++i)
+                       targetmap = targetmap->next;
+               currentmap = targetmap;
+               newdata = (unsigned char *)Mem_Alloc(font_mempool, datasize * M);
+               for (i = 0; i < M; ++i)
+               {
+                       chunk = incmap->data_tier1[i];
+                       if (chunk == NULL)
+                               continue;
+                       for (y = 0; y < datasize; y += pitch)
+                               for (x = 0; x < pitch; ++x)
+                                       newdata[y * M + i * pitch + x] = chunk[y + x];
+                       Mem_Free(chunk);
+                       incmap->data_tier1[i] = NULL;
+                       merge_single_map(targetmap, i, currentmap, 0);
+                       fix_glyph_coords_tier1(&targetmap->glyphs[i], (float)i);
+                       currentmap = currentmap->next;
+               }
+               update_pic_for_fontmap(targetmap, Draw_GetPicName(targetmap->pic), width * M, height, newdata);
+               UnloadMapChain(targetmap->next);
+               targetmap->next = NULL;
+               incmap->data_tier2[tier2_data_index] = newdata;
+               ++incmap->tier1_merged;
+               incmap->tier1_merged %= M;
+               incmap->newmap_start = INCMAP_START + targetmap_at + 1;
+               // then give this merged map
+               *outmap = targetmap;
+               *outmapch = FONT_CHARS_PER_LINE - 1;
+       }
+       if (index % (M * N) == M * N - 1)
+       {
+               // tier 2 reduction, lines to full map
+               calc_data_arguments(M, 1);
+               targetmap_at = incmap->tier2_merged;
+               targetmap = startmap;
+               for (i = 0; i < targetmap_at; ++i)
+                       targetmap = targetmap->next;
+               currentmap = targetmap;
+               newdata = (unsigned char *)Mem_Alloc(font_mempool, datasize * N);
+               for (i = 0; i < N; ++i)
+               {
+                       chunk = incmap->data_tier2[i];
+                       if (chunk == NULL)
+                               continue;
+                       for (x = 0; x < datasize; ++x)
+                               newdata[i * datasize + x] = chunk[x];
+                       Mem_Free(chunk);
+                       incmap->data_tier2[i] = NULL;
+                       for (j = 0; j < M; ++j)
+                       {
+                               merge_single_map(targetmap, i * M + j, currentmap, j);
+                               fix_glyph_coords_tier2(&targetmap->glyphs[i * M + j], (float)i);
+                       }
+                       currentmap = currentmap->next;
+               }
+               update_pic_for_fontmap(targetmap, Draw_GetPicName(targetmap->pic), width, height * N, newdata);
+               UnloadMapChain(targetmap->next);
+               targetmap->next = NULL;
+               Mem_Free(newdata);
+               ++incmap->tier2_merged;
+               incmap->newmap_start = INCMAP_START + targetmap_at + 1;
+               // then give this merged map
+               *outmap = targetmap;
+               *outmapch = FONT_CHARS_PER_MAP - 1;
+       }
+
+       ++incmap->charcount;
+       ++incmap->newmap_start;
+
+       #undef M
+       #undef N
+}
+
+static qbool Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch,
+               ft2_font_map_t **outmap, int *outmapch, qbool use_incmap)
+{
+       #define bytes_per_pixel 4
+
        char map_identifier[MAX_QPATH];
-       unsigned long mapidx = _ch / FONT_CHARS_PER_MAP;
+       unsigned long map_startglyph = _ch / FONT_CHARS_PER_MAP * FONT_CHARS_PER_MAP;
        unsigned char *data = NULL;
-       FT_ULong ch, mapch;
+       FT_ULong ch = 0, mapch = 0;
        int status;
        int tp;
        FT_Int32 load_flags;
        int gpad_l, gpad_r, gpad_t, gpad_b;
-       char vabuf[1024];
 
        int pitch;
-       int gR, gC; // glyph position: row and column
+       int width, height, datasize;
+       int glyph_row, glyph_column;
+
+       int chars_per_line = FONT_CHARS_PER_LINE;
+       int char_lines = FONT_CHAR_LINES;
+       int chars_per_map = FONT_CHARS_PER_MAP;
 
-       ft2_font_map_t *map, *next;
        ft2_font_t *usefont;
+       ft2_font_map_t *map, *next;
+       font_incmap_t *incmap;
 
        FT_Face fontface;
 
-       int bytesPerPixel = 4; // change the conversion loop too if you change this!
-
-       if (outmap)
-               *outmap = NULL;
-
-       if (r_font_use_alpha_textures.integer)
-               bytesPerPixel = 1;
+       incmap = mapstart->incmap;
+       if (use_incmap)
+       {
+               // only render one character in this map;
+               // such small maps will be merged together later in `incmap_post_process`
+               chars_per_line = char_lines = chars_per_map = 1;
+               // and the index is incremental
+               map_startglyph = incmap ? incmap->newmap_start : INCMAP_START;
+       }
 
        if (font->image_font)
                fontface = (FT_Face)font->next->face;
@@ -1311,13 +1503,16 @@ static qbool Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch,
        map = (ft2_font_map_t *)Mem_Alloc(font_mempool, sizeof(ft2_font_map_t));
        if (!map)
        {
-               Con_Printf(CON_ERROR "ERROR: Out of memory when loading fontmap for %s\n", font->name);
+               Con_Printf(CON_ERROR "ERROR: Out of memory when allocating fontmap for %s\n", font->name);
                return false;
        }
 
-       // create a totally unique name for this map, then we will use it to make a unique cachepic_t to avoid redundant textures
+       map->start = map_startglyph;
+
+       // create a unique name for this map, then we will use it to make a unique cachepic_t to avoid redundant textures
+       /*
        dpsnprintf(map_identifier, sizeof(map_identifier),
-               "%s_cache_%g_%d_%g_%g_%g_%g_%g_%u",
+               "%s_cache_%g_%d_%g_%g_%g_%g_%g_%u_%lx",
                font->name,
                (double) mapstart->intSize,
                (int) load_flags,
@@ -1326,13 +1521,26 @@ static qbool Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch,
                (double) font->settings->shadowx,
                (double) font->settings->shadowy,
                (double) font->settings->shadowz,
-               (unsigned) mapidx);
+               (unsigned) map_startglyph,
+               // add pointer as a unique part to avoid earlier incmaps' state being trashed
+               use_incmap ? (unsigned long)mapstart : 0x0);
+       */
+       /*
+        * note 1: it appears that different font instances may have the same metrics, causing this pic being overwritten
+        *         will use startmap's pointer as a unique part to avoid earlier incmaps' dynamic pics being trashed
+        * note 2: if this identifier is made too long, significient performance drop will take place
+        * note 3: blur/outline/shadow are per-font settings, so a pointer to startmap & map size
+        *         already made these unique, hence they are omitted
+        * note 4: font_diskcache is removed, this name can be less meaningful
+        */
+       dpsnprintf(map_identifier, sizeof(map_identifier), "%s_%g_%p_%u",
+                       font->name, mapstart->intSize, mapstart, (unsigned) map_startglyph);
 
        // create a cachepic_t from the data now, or reuse an existing one
        if (developer_font.integer)
                Con_Printf("Generating font map %s (size: %.1f MB)\n", map_identifier, mapstart->glyphSize * (256 * 4 / 1048576.0) * mapstart->glyphSize);
 
-       Font_Postprocess(font, NULL, 0, bytesPerPixel, mapstart->size*2, mapstart->size*2, &gpad_l, &gpad_r, &gpad_t, &gpad_b);
+       Font_Postprocess(font, NULL, 0, bytes_per_pixel, mapstart->size*2, mapstart->size*2, &gpad_l, &gpad_r, &gpad_t, &gpad_b);
 
        // copy over the information
        map->size = mapstart->size;
@@ -1341,19 +1549,57 @@ static qbool Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch,
        map->sfx = mapstart->sfx;
        map->sfy = mapstart->sfy;
 
-       pitch = map->glyphSize * FONT_CHARS_PER_LINE * bytesPerPixel;
-       data = (unsigned char *)Mem_Alloc(font_mempool, (FONT_CHAR_LINES * map->glyphSize) * pitch);
+       width = map->glyphSize * chars_per_line;
+       height = map->glyphSize * char_lines;
+       pitch = width * bytes_per_pixel;
+       datasize = height * pitch;
+       data = (unsigned char *)Mem_Alloc(font_mempool, datasize);
        if (!data)
        {
                Con_Printf(CON_ERROR "ERROR: Failed to allocate memory for font %s size %g\n", font->name, map->size);
                Mem_Free(map);
                return false;
        }
+
+       if (use_incmap)
+       {
+               if (mapstart->incmap == NULL)
+               {
+                       // initial incmap
+                       incmap = mapstart->incmap = (font_incmap_t *)Mem_Alloc(font_mempool, sizeof(font_incmap_t));
+                       if (!incmap)
+                       {
+                               Con_Printf(CON_ERROR "ERROR: Out of memory when allocating incremental fontmap for %s\n", font->name);
+                               return false;
+                       }
+                       // this will be the startmap of incmap
+                       incmap->fontmap = map;
+                       incmap->newmap_start = INCMAP_START;
+               }
+               else
+               {
+                       // new maps for incmap shall always be the last one
+                       next = incmap->fontmap;
+                       while (next->next != NULL)
+                               next = next->next;
+                       next->next = map;
+               }
+       }
+       else
+       {
+               // insert this normal map
+               next = use_incmap ? incmap->fontmap : mapstart;
+               while(next->next && next->next->start < map->start)
+                       next = next->next;
+               map->next = next->next;
+               next->next = map;
+       }
+
        // initialize as white texture with zero alpha
        tp = 0;
-       while (tp < (FONT_CHAR_LINES * map->glyphSize) * pitch)
+       while (tp < datasize)
        {
-               if (bytesPerPixel == 4)
+               if (bytes_per_pixel == 4)
                {
                        data[tp++] = 0xFF;
                        data[tp++] = 0xFF;
@@ -1362,21 +1608,11 @@ static qbool Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch,
                data[tp++] = 0x00;
        }
 
-       memset(map->width_of, 0, sizeof(map->width_of));
-
-       // insert the map
-       map->start = mapidx * FONT_CHARS_PER_MAP;
-       next = mapstart;
-       while(next->next && next->next->start < map->start)
-               next = next->next;
-       map->next = next->next;
-       next->next = map;
-
-       gR = 0;
-       gC = -1;
-       for (ch = map->start;
-            ch < (FT_ULong)map->start + FONT_CHARS_PER_MAP;
-            ++ch)
+       glyph_row = 0;
+       glyph_column = 0;
+       ch = (FT_ULong)(use_incmap ? _ch : map->start);
+       mapch = 0;
+       while (true)
        {
                FT_ULong glyphIndex;
                int w, h, x, y;
@@ -1387,22 +1623,15 @@ static qbool Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch,
                FT_Face face;
                int pad_l, pad_r, pad_t, pad_b;
 
-               mapch = ch - map->start;
-
                if (developer_font.integer)
                        Con_DPrint("glyphinfo: ------------- GLYPH INFO -----------------\n");
 
-               ++gC;
-               if (gC >= FONT_CHARS_PER_LINE)
-               {
-                       gC -= FONT_CHARS_PER_LINE;
-                       ++gR;
-               }
+               map->glyphchars[mapch] = (Uchar)ch;
 
                if (data)
                {
-                       imagedata = data + gR * pitch * map->glyphSize + gC * map->glyphSize * bytesPerPixel;
-                       imagedata += gpad_t * pitch + gpad_l * bytesPerPixel;
+                       imagedata = data + glyph_row * pitch * map->glyphSize + glyph_column * map->glyphSize * bytes_per_pixel;
+                       imagedata += gpad_t * pitch + gpad_l * bytes_per_pixel;
                }
                //status = qFT_Load_Char(face, ch, FT_LOAD_RENDER);
                // we need the glyphIndex
@@ -1503,44 +1732,44 @@ static qbool Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch,
                                switch (bmp->pixel_mode)
                                {
                                case FT_PIXEL_MODE_MONO:
-                                       dst += bytesPerPixel - 1; // shift to alpha byte
+                                       dst += bytes_per_pixel - 1; // shift to alpha byte
                                        for (x = 0; x < bmp->width; x += 8)
                                        {
                                                unsigned char c = *src++;
-                                               *dst = 255 * !!((c & 0x80) >> 7); dst += bytesPerPixel;
-                                               *dst = 255 * !!((c & 0x40) >> 6); dst += bytesPerPixel;
-                                               *dst = 255 * !!((c & 0x20) >> 5); dst += bytesPerPixel;
-                                               *dst = 255 * !!((c & 0x10) >> 4); dst += bytesPerPixel;
-                                               *dst = 255 * !!((c & 0x08) >> 3); dst += bytesPerPixel;
-                                               *dst = 255 * !!((c & 0x04) >> 2); dst += bytesPerPixel;
-                                               *dst = 255 * !!((c & 0x02) >> 1); dst += bytesPerPixel;
-                                               *dst = 255 * !!((c & 0x01) >> 0); dst += bytesPerPixel;
+                                               *dst = 255 * !!((c & 0x80) >> 7); dst += bytes_per_pixel;
+                                               *dst = 255 * !!((c & 0x40) >> 6); dst += bytes_per_pixel;
+                                               *dst = 255 * !!((c & 0x20) >> 5); dst += bytes_per_pixel;
+                                               *dst = 255 * !!((c & 0x10) >> 4); dst += bytes_per_pixel;
+                                               *dst = 255 * !!((c & 0x08) >> 3); dst += bytes_per_pixel;
+                                               *dst = 255 * !!((c & 0x04) >> 2); dst += bytes_per_pixel;
+                                               *dst = 255 * !!((c & 0x02) >> 1); dst += bytes_per_pixel;
+                                               *dst = 255 * !!((c & 0x01) >> 0); dst += bytes_per_pixel;
                                        }
                                        break;
                                case FT_PIXEL_MODE_GRAY2:
-                                       dst += bytesPerPixel - 1; // shift to alpha byte
+                                       dst += bytes_per_pixel - 1; // shift to alpha byte
                                        for (x = 0; x < bmp->width; x += 4)
                                        {
                                                unsigned char c = *src++;
-                                               *dst = ( ((c & 0xA0) >> 6) * 0x55 ); c <<= 2; dst += bytesPerPixel;
-                                               *dst = ( ((c & 0xA0) >> 6) * 0x55 ); c <<= 2; dst += bytesPerPixel;
-                                               *dst = ( ((c & 0xA0) >> 6) * 0x55 ); c <<= 2; dst += bytesPerPixel;
-                                               *dst = ( ((c & 0xA0) >> 6) * 0x55 ); c <<= 2; dst += bytesPerPixel;
+                                               *dst = ( ((c & 0xA0) >> 6) * 0x55 ); c <<= 2; dst += bytes_per_pixel;
+                                               *dst = ( ((c & 0xA0) >> 6) * 0x55 ); c <<= 2; dst += bytes_per_pixel;
+                                               *dst = ( ((c & 0xA0) >> 6) * 0x55 ); c <<= 2; dst += bytes_per_pixel;
+                                               *dst = ( ((c & 0xA0) >> 6) * 0x55 ); c <<= 2; dst += bytes_per_pixel;
                                        }
                                        break;
                                case FT_PIXEL_MODE_GRAY4:
-                                       dst += bytesPerPixel - 1; // shift to alpha byte
+                                       dst += bytes_per_pixel - 1; // shift to alpha byte
                                        for (x = 0; x < bmp->width; x += 2)
                                        {
                                                unsigned char c = *src++;
-                                               *dst = ( ((c & 0xF0) >> 4) * 0x11); dst += bytesPerPixel;
-                                               *dst = ( ((c & 0x0F) ) * 0x11); dst += bytesPerPixel;
+                                               *dst = ( ((c & 0xF0) >> 4) * 0x11); dst += bytes_per_pixel;
+                                               *dst = ( ((c & 0x0F) ) * 0x11); dst += bytes_per_pixel;
                                        }
                                        break;
                                case FT_PIXEL_MODE_GRAY:
                                        // in this case pitch should equal width
                                        for (tp = 0; tp < bmp->pitch; ++tp)
-                                               dst[(bytesPerPixel - 1) + tp*bytesPerPixel] = src[tp]; // copy the grey value into the alpha bytes
+                                               dst[(bytes_per_pixel - 1) + tp*bytes_per_pixel] = src[tp]; // copy the grey value into the alpha bytes
 
                                        //memcpy((void*)dst, (void*)src, bmp->pitch);
                                        //dst += bmp->pitch;
@@ -1554,7 +1783,7 @@ static qbool Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch,
                        pad_r = gpad_r;
                        pad_t = gpad_t;
                        pad_b = gpad_b;
-                       Font_Postprocess(font, imagedata, pitch, bytesPerPixel, w, h, &pad_l, &pad_r, &pad_t, &pad_b);
+                       Font_Postprocess(font, imagedata, pitch, bytes_per_pixel, w, h, &pad_l, &pad_r, &pad_t, &pad_b);
                }
                else
                {
@@ -1562,7 +1791,7 @@ static qbool Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch,
                        pad_r = gpad_r;
                        pad_t = gpad_t;
                        pad_b = gpad_b;
-                       Font_Postprocess(font, NULL, pitch, bytesPerPixel, w, h, &pad_l, &pad_r, &pad_t, &pad_b);
+                       Font_Postprocess(font, NULL, pitch, bytes_per_pixel, w, h, &pad_l, &pad_r, &pad_t, &pad_b);
                }
 
 
@@ -1579,10 +1808,10 @@ static qbool Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch,
                        //double mWidth = (glyph->metrics.width >> 6) / map->size;
                        //double mHeight = (glyph->metrics.height >> 6) / map->size;
 
-                       mapglyph->txmin = ( (double)(gC * map->glyphSize) + (double)(gpad_l - pad_l) ) / ( (double)(map->glyphSize * FONT_CHARS_PER_LINE) );
-                       mapglyph->txmax = mapglyph->txmin + (double)(bmp->width + pad_l + pad_r) / ( (double)(map->glyphSize * FONT_CHARS_PER_LINE) );
-                       mapglyph->tymin = ( (double)(gR * map->glyphSize) + (double)(gpad_r - pad_r) ) / ( (double)(map->glyphSize * FONT_CHAR_LINES) );
-                       mapglyph->tymax = mapglyph->tymin + (double)(bmp->rows + pad_t + pad_b) / ( (double)(map->glyphSize * FONT_CHAR_LINES) );
+                       mapglyph->txmin = ( (double)(glyph_column * map->glyphSize) + (double)(gpad_l - pad_l) ) / ( (double)(map->glyphSize * chars_per_line) );
+                       mapglyph->txmax = mapglyph->txmin + (double)(bmp->width + pad_l + pad_r) / ( (double)(map->glyphSize * chars_per_line) );
+                       mapglyph->tymin = ( (double)(glyph_row * map->glyphSize) + (double)(gpad_r - pad_r) ) / ( (double)(map->glyphSize * char_lines) );
+                       mapglyph->tymax = mapglyph->tymin + (double)(bmp->rows + pad_t + pad_b) / ( (double)(map->glyphSize * char_lines) );
                        //mapglyph->vxmin = bearingX;
                        //mapglyph->vxmax = bearingX + mWidth;
                        mapglyph->vxmin = (glyph->bitmap_left - pad_l) / map->size;
@@ -1599,7 +1828,7 @@ static qbool Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch,
 
                        if (developer_font.integer)
                        {
-                               Con_DPrintf("glyphinfo:   Glyph: %lu   at (%i, %i)\n", (unsigned long)ch, gC, gR);
+                               Con_DPrintf("glyphinfo:   Glyph: %lu   at (%i, %i)\n", (unsigned long)ch, glyph_column, glyph_row);
                                Con_DPrintf("glyphinfo:   %f, %f, %lu\n", bearingX, map->sfx, (unsigned long)glyph->metrics.horiBearingX);
                                if (ch >= 32 && ch <= 128)
                                        Con_DPrintf("glyphinfo:   Character: %c\n", (int)ch);
@@ -1613,36 +1842,19 @@ static qbool Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch,
                        }
                }
                map->glyphs[mapch].image = false;
-       }
 
-       {
-               int w = map->glyphSize * FONT_CHARS_PER_LINE;
-               int h = map->glyphSize * FONT_CHAR_LINES;
-               // update the pic returned by Draw_CachePic_Flags earlier to contain our texture
-               map->pic = Draw_NewPic(map_identifier, w, h, data, r_font_use_alpha_textures.integer ? TEXTYPE_ALPHA : TEXTYPE_RGBA, TEXF_ALPHA | TEXF_CLAMP | (r_font_compress.integer > 0 ? TEXF_COMPRESS : 0));
-
-               if (r_font_diskcache.integer >= 1)
+               ++mapch; ++ch;
+               if ((int)mapch == chars_per_map)
+                       break;
+               if (++glyph_column % chars_per_line == 0)
                {
-                       // swap to BGRA for tga writing...
-                       int s = w * h;
-                       int x;
-                       int b;
-                       for (x = 0;x < s;x++)
-                       {
-                               b = data[x*4+0];
-                               data[x*4+0] = data[x*4+2];
-                               data[x*4+2] = b;
-                       }
-                       Image_WriteTGABGRA(va(vabuf, sizeof(vabuf), "%s.tga", map_identifier), w, h, data);
-#ifndef USE_GLES2
-                       if (r_font_compress.integer && Draw_IsPicLoaded(map->pic))
-                               R_SaveTextureDDSFile(Draw_GetPicTexture(map->pic), va(vabuf, sizeof(vabuf), "dds/%s.dds", map_identifier), r_texture_dds_save.integer < 2, true);
-#endif
+                       glyph_column = 0;
+                       ++glyph_row;
                }
        }
 
-       if(data)
-               Mem_Free(data);
+       // update the pic returned by Draw_CachePic_Flags earlier to contain our texture
+       update_pic_for_fontmap(map, map_identifier, width, height, data);
 
        if (!Draw_IsPicLoaded(map->pic))
        {
@@ -1651,29 +1863,150 @@ static qbool Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch,
                // this would be bad...
                // only `data' must be freed
                Con_Printf(CON_ERROR "ERROR: Failed to generate texture for font %s size %f map %lu\n",
-                          font->name, mapstart->size, mapidx);
+                          font->name, mapstart->size, map_startglyph);
                return false;
        }
-       if (outmap)
+
+       if (use_incmap)
+       {
                *outmap = map;
+               *outmapch = 0;
+               // data will be kept in incmap for being merged later, freed afterward
+               incmap_post_process(incmap, _ch, data, outmap, outmapch);
+       }
+       else if (data)
+       {
+               Mem_Free(data);
+               *outmap = map;
+               if (outmapch != NULL)
+                       *outmapch = _ch - map->start;
+       }
+
        return true;
 }
 
-qbool Font_LoadMapForIndex(ft2_font_t *font, int map_index, Uchar _ch, ft2_font_map_t **outmap)
+static qbool legacy_font_loading_api_alerted = false;
+static inline void alert_legacy_font_api(const char *name)
+{
+       if (!legacy_font_loading_api_alerted)
+       {
+               Con_DPrintf(CON_WARN "Warning: You are using an legacy API '%s', which have certain limitations; please use 'Font_GetMapForChar' instead\n", name);
+               legacy_font_loading_api_alerted = true;
+       }
+}
+
+// legacy font API, please use `Font_GetMapForChar` instead
+qbool Font_LoadMapForIndex(ft2_font_t *font, int map_index, Uchar ch, ft2_font_map_t **outmap)
 {
        if (map_index < 0 || map_index >= MAX_FONT_SIZES)
                return false;
        // the first map must have been loaded already
        if (!font->font_maps[map_index])
                return false;
-       return Font_LoadMap(font, font->font_maps[map_index], _ch, outmap);
+       alert_legacy_font_api("Font_LoadMapForIndex");
+       return Font_LoadMap(font, font->font_maps[map_index], ch, outmap, NULL, false);
 }
 
+// legacy font API. please use `Font_GetMapForChar` instead
 ft2_font_map_t *FontMap_FindForChar(ft2_font_map_t *start, Uchar ch)
 {
-       while (start && start->start + FONT_CHARS_PER_MAP <= ch)
-               start = start->next;
-       if (start && start->start > ch)
+       ft2_font_map_t *map = start;
+       while (map && map->start + FONT_CHARS_PER_MAP <= ch)
+               map = map->next;
+       if (map && map->start > ch)
                return NULL;
-       return start;
+       alert_legacy_font_api("FontMap_FindForChar");
+       return map;
+}
+
+static inline qbool should_use_incmap(Uchar ch)
+{
+       int i;
+       // optimize: a simple check logic for usual conditions
+       if (ch < unicode_bigblocks[0])
+               return false;
+       if (r_font_disable_incmaps.integer == 1)
+               return false;
+       for (i = 0; i < (int)(sizeof(unicode_bigblocks) / sizeof(Uchar)); i += 2)
+               if (unicode_bigblocks[i] <= ch && ch <= unicode_bigblocks[i + 1])
+                       return true;
+       return false;
+}
+
+static inline qbool get_char_from_incmap(ft2_font_map_t *map, Uchar ch, ft2_font_map_t **outmap, int *outmapch)
+{
+       int i;
+       font_incmap_t *incmap;
+
+       incmap = map->incmap;
+       *outmapch = 0;
+
+       if (incmap != NULL)
+       {
+               map = incmap->fontmap;
+               while (map != NULL)
+               {
+                       for (i = 0; i < FONT_CHARS_PER_MAP; ++i)
+                       {
+                               if (map->glyphchars[i] == ch)
+                               {
+                                       *outmap = map;
+                                       *outmapch = i;
+                                       return true;
+                               }
+                               else if (map->glyphchars[i] == 0)
+                                       // this tier0/tier1 map ends here
+                                       break;
+                       }
+                       map = map->next;
+               }
+       }
+       return false;
+}
+
+/**
+ * Query for or load a font map for a character, with the character's place on it.
+ * Supports the incremental map mechanism; returning if the operation is done successfully
+ */
+qbool Font_GetMapForChar(ft2_font_t *font, int map_index, Uchar ch, ft2_font_map_t **outmap, int *outmapch)
+{
+       qbool use_incmap;
+       ft2_font_map_t *map;
+
+       // startmap
+       map = Font_MapForIndex(font, map_index);
+
+       // optimize: the first map must have been loaded already
+       if (ch < FONT_CHARS_PER_MAP)
+       {
+               *outmapch = ch;
+               *outmap = map;
+               return true;
+       }
+
+       // search for the character
+
+       use_incmap = should_use_incmap(ch);
+
+       if (!use_incmap)
+       {
+               // normal way
+               *outmapch = ch % FONT_CHARS_PER_MAP;
+               while (map && map->start + FONT_CHARS_PER_MAP <= ch)
+                       map = map->next;
+               if (map && map->start <= ch)
+               {
+                       *outmap = map;
+                       return true;
+               }
+       }
+       else if (get_char_from_incmap(map, ch, outmap, outmapch))
+               // got it
+               return true;
+
+       // so no appropriate map was found, load one
+
+       if (map_index < 0 || map_index >= MAX_FONT_SIZES)
+               return false;
+       return Font_LoadMap(font, font->font_maps[map_index], ch, outmap, outmapch, use_incmap);
 }
diff --git a/ft2.h b/ft2.h
index 2104a8c4fd9f6620afeb7fb231d77fad5a075663..6495d3eb57b8f603c2c02995a950592272ac92d4 100644 (file)
--- a/ft2.h
+++ b/ft2.h
@@ -21,6 +21,8 @@
  */
 
 typedef struct ft2_font_map_s ft2_font_map_t;
+typedef struct font_incmap_s font_incmap_t;
+typedef struct incmap_lookup_cache_s incmap_lookup_cache_t;
 typedef struct ft2_attachment_s ft2_attachment_t;
 #ifdef WIN64
 #define ft2_oldstyle_map ((ft2_font_map_t*)-1LL)
@@ -36,38 +38,38 @@ typedef struct ft2_kerning_s
 
 typedef struct ft2_font_s
 {
-       char            name[64];
-       qbool        has_kerning;
+       char   name[64];
+       qbool  has_kerning;
        // last requested size loaded using Font_SetSize
-       float           currentw;
-       float           currenth;
-       float           ascend;
-       float           descend;
-       qbool        image_font; // only fallbacks are freetype fonts
+       float  currentw;
+       float  currenth;
+       float  ascend;
+       float  descend;
+       qbool  image_font; // only fallbacks are freetype fonts
 
        // TODO: clean this up and do not expose everything.
-       
+
        const unsigned char  *data; // FT2 needs it to stay
        //fs_offset_t     datasize;
-       void           *face;
+       void                 *face;
 
        // an unordered array of ordered linked lists of glyph maps for a specific size
-       ft2_font_map_t *font_maps[MAX_FONT_SIZES];
-       int             num_sizes;
+       ft2_font_map_t       *font_maps[MAX_FONT_SIZES];
+       int                   num_sizes;
 
        // attachments
-       size_t            attachmentcount;
-       ft2_attachment_t *attachments;
+       size_t                attachmentcount;
+       ft2_attachment_t     *attachments;
 
-       ft2_settings_t *settings;
+       ft2_settings_t       *settings;
 
        // fallback mechanism
-       struct ft2_font_s *next;
+       struct ft2_font_s    *next;
 } ft2_font_t;
 
 void            Font_CloseLibrary(void);
 void            Font_Init(void);
-qbool        Font_OpenLibrary(void);
+qbool           Font_OpenLibrary(void);
 ft2_font_t*     Font_Alloc(void);
 void            Font_UnloadFont(ft2_font_t *font);
 // IndexForSize suggests to change the width and height if a font size is in a reasonable range
@@ -75,11 +77,12 @@ void            Font_UnloadFont(ft2_font_t *font);
 // in such a case, *outw and *outh are set to 12, which is often a good alternative size
 int             Font_IndexForSize(ft2_font_t *font, float size, float *outw, float *outh);
 ft2_font_map_t *Font_MapForIndex(ft2_font_t *font, int index);
-qbool        Font_LoadFont(const char *name, dp_font_t *dpfnt);
-qbool        Font_GetKerningForSize(ft2_font_t *font, float w, float h, Uchar left, Uchar right, float *outx, float *outy);
-qbool        Font_GetKerningForMap(ft2_font_t *font, int map_index, float w, float h, Uchar left, Uchar right, float *outx, float *outy);
+qbool           Font_LoadFont(const char *name, dp_font_t *dpfnt);
+qbool           Font_GetKerningForSize(ft2_font_t *font, float w, float h, Uchar left, Uchar right, float *outx, float *outy);
+qbool           Font_GetKerningForMap(ft2_font_t *font, int map_index, float w, float h, Uchar left, Uchar right, float *outx, float *outy);
 float           Font_VirtualToRealSize(float sz);
 float           Font_SnapTo(float val, float snapwidth);
 // since this is used on a font_map_t, let's name it FontMap_*
 ft2_font_map_t *FontMap_FindForChar(ft2_font_map_t *start, Uchar ch);
+qbool           Font_GetMapForChar(ft2_font_t *font, int map_index, Uchar ch, ft2_font_map_t **outmap, int *outmapch);
 #endif // DP_FREETYPE2_H__
index 8cdc9e1d0ceb3ac6240e1a8ef9e16e51fa0a0e36..68956b9082d6682c42c7b8492bd2801155071502 100644 (file)
@@ -7,6 +7,9 @@
 #define FONT_CHAR_LINES 16
 #define FONT_CHARS_PER_MAP (FONT_CHARS_PER_LINE * FONT_CHAR_LINES)
 
+// map.start value for incremental maps to hold a place
+#define INCMAP_START 0x110000
+
 typedef struct glyph_slot_s
 {
        qbool image;
@@ -26,32 +29,52 @@ typedef struct glyph_slot_s
 
 struct ft2_font_map_s
 {
-       Uchar                  start;
-       struct ft2_font_map_s *next;
-       float                  size;
+       Uchar  start;
+       float  size;
+
        // the actual size used in the freetype code
        // by convention, the requested size is the height of the font's bounding box.
-       float                  intSize;
-       int                    glyphSize;
+       float  intSize;
+       int    glyphSize;
+
+       ft2_font_map_t *next;
+       cachepic_t     *pic;
+       qbool           static_tex;
+       glyph_slot_t    glyphs[FONT_CHARS_PER_MAP];
+       Uchar           glyphchars[FONT_CHARS_PER_MAP];
+
+       // saves us the trouble of calculating these over and over again
+       double          sfx, sfy;
 
-       cachepic_t            *pic;
-       qbool               static_tex;
-       glyph_slot_t           glyphs[FONT_CHARS_PER_MAP];
+       // note: float width_of[256] was moved to `struct dp_font_s` as width_of_ft2
 
+       // these may only present in a startmap
        // contains the kerning information for the first 256 characters
        // for the other characters, we will lookup the kerning information
-       ft2_kerning_t          kerning;
-       // safes us the trouble of calculating these over and over again
-       double                 sfx, sfy;
+       ft2_kerning_t  *kerning;
+       // for accessing incremental maps for bigblock glyphs
+       font_incmap_t  *incmap;
+};
+
+struct font_incmap_s
+{
+       // associated fontmap; startmap of incmaps
+       struct ft2_font_map_s *fontmap;
+       int charcount;
+       int newmap_start;
+
+       // two rounds of merge will take place, keep those data until then
+       unsigned char *data_tier1[FONT_CHARS_PER_LINE];
+       unsigned char *data_tier2[FONT_CHAR_LINES];
 
-       // the width_of for the image-font, pixel-snapped for this size
-       float           width_of[256];
+       // count of merged maps
+       int tier1_merged, tier2_merged;
 };
 
 struct ft2_attachment_s
 {
        const unsigned char *data;
-       fs_offset_t    size;
+       fs_offset_t          size;
 };
 
 //qbool Font_LoadMapForIndex(ft2_font_t *font, Uchar _ch, ft2_font_map_t **outmap);
index ad4a0cdc21ae05de04e3d487eacfa9f7f822887f..b5336a226223f62d852b5760a5b8e534117c8546 100644 (file)
@@ -27,9 +27,7 @@ float gl_modelviewprojection16f[16];
 qbool gl_modelmatrixchanged;
 
 #ifdef DEBUGGL
-int gl_errornumber = 0;
-
-void GL_PrintError(int errornumber, const char *filename, int linenumber)
+void GL_PrintError(GLenum errornumber, const char *filename, unsigned int linenumber)
 {
        switch(errornumber)
        {
@@ -972,28 +970,28 @@ int R_Mesh_CreateFramebufferObject(rtexture_t *depthtexture, rtexture_t *colorte
                // GL_ARB_framebuffer_object (GL3-class hardware) - depth stencil attachment
 #ifdef USE_GLES2
                // FIXME: separate stencil attachment on GLES
-               if (depthtexture  && depthtexture->texnum ) qglFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT  , depthtexture->gltexturetypeenum , depthtexture->texnum , 0);CHECKGLERROR
-               if (depthtexture  && depthtexture->renderbuffernum ) qglFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT  , GL_RENDERBUFFER, depthtexture->renderbuffernum );CHECKGLERROR
+               if (depthtexture  && depthtexture->texnum ) { qglFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT  , depthtexture->gltexturetypeenum , depthtexture->texnum , 0);CHECKGLERROR }
+               if (depthtexture  && depthtexture->renderbuffernum ) { qglFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT  , GL_RENDERBUFFER, depthtexture->renderbuffernum );CHECKGLERROR }
 #else
                if (depthtexture  && depthtexture->texnum )
                {
                        qglFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT  , depthtexture->gltexturetypeenum , depthtexture->texnum , 0);CHECKGLERROR
-                       if (depthtexture->glisdepthstencil) qglFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT  , depthtexture->gltexturetypeenum , depthtexture->texnum , 0);CHECKGLERROR
+                       if (depthtexture->glisdepthstencil) { qglFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT  , depthtexture->gltexturetypeenum , depthtexture->texnum , 0);CHECKGLERROR }
                }
                if (depthtexture  && depthtexture->renderbuffernum )
                {
                        qglFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT  , GL_RENDERBUFFER, depthtexture->renderbuffernum );CHECKGLERROR
-                       if (depthtexture->glisdepthstencil) qglFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT  , GL_RENDERBUFFER, depthtexture->renderbuffernum );CHECKGLERROR
+                       if (depthtexture->glisdepthstencil) { qglFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT  , GL_RENDERBUFFER, depthtexture->renderbuffernum );CHECKGLERROR }
                }
 #endif
-               if (colortexture  && colortexture->texnum ) qglFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 , colortexture->gltexturetypeenum , colortexture->texnum , 0);CHECKGLERROR
-               if (colortexture2 && colortexture2->texnum) qglFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1 , colortexture2->gltexturetypeenum, colortexture2->texnum, 0);CHECKGLERROR
-               if (colortexture3 && colortexture3->texnum) qglFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2 , colortexture3->gltexturetypeenum, colortexture3->texnum, 0);CHECKGLERROR
-               if (colortexture4 && colortexture4->texnum) qglFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT3 , colortexture4->gltexturetypeenum, colortexture4->texnum, 0);CHECKGLERROR
-               if (colortexture  && colortexture->renderbuffernum ) qglFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 , GL_RENDERBUFFER, colortexture->renderbuffernum );CHECKGLERROR
-               if (colortexture2 && colortexture2->renderbuffernum) qglFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1 , GL_RENDERBUFFER, colortexture2->renderbuffernum);CHECKGLERROR
-               if (colortexture3 && colortexture3->renderbuffernum) qglFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2 , GL_RENDERBUFFER, colortexture3->renderbuffernum);CHECKGLERROR
-               if (colortexture4 && colortexture4->renderbuffernum) qglFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT3 , GL_RENDERBUFFER, colortexture4->renderbuffernum);CHECKGLERROR
+               if (colortexture  && colortexture->texnum ) { qglFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 , colortexture->gltexturetypeenum , colortexture->texnum , 0);CHECKGLERROR }
+               if (colortexture2 && colortexture2->texnum) { qglFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1 , colortexture2->gltexturetypeenum, colortexture2->texnum, 0);CHECKGLERROR }
+               if (colortexture3 && colortexture3->texnum) { qglFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2 , colortexture3->gltexturetypeenum, colortexture3->texnum, 0);CHECKGLERROR }
+               if (colortexture4 && colortexture4->texnum) { qglFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT3 , colortexture4->gltexturetypeenum, colortexture4->texnum, 0);CHECKGLERROR }
+               if (colortexture  && colortexture->renderbuffernum ) { qglFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 , GL_RENDERBUFFER, colortexture->renderbuffernum );CHECKGLERROR }
+               if (colortexture2 && colortexture2->renderbuffernum) { qglFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1 , GL_RENDERBUFFER, colortexture2->renderbuffernum);CHECKGLERROR }
+               if (colortexture3 && colortexture3->renderbuffernum) { qglFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2 , GL_RENDERBUFFER, colortexture3->renderbuffernum);CHECKGLERROR }
+               if (colortexture4 && colortexture4->renderbuffernum) { qglFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT3 , GL_RENDERBUFFER, colortexture4->renderbuffernum);CHECKGLERROR }
 
 #ifndef USE_GLES2
                if (colortexture4)
@@ -1775,7 +1773,7 @@ r_meshbuffer_t *R_Mesh_CreateMeshBuffer(const void *data, size_t size, const cha
        buffer->isuniformbuffer = isuniformbuffer;
        buffer->isdynamic = isdynamic;
        buffer->isindex16 = isindex16;
-       strlcpy(buffer->name, name, sizeof(buffer->name));
+       dp_strlcpy(buffer->name, name, sizeof(buffer->name));
        R_Mesh_UpdateMeshBuffer(buffer, data, size, false, 0);
        return buffer;
 }
index b4978abb1e4715a803a8eaae17b452c569705e4a..d6b6a4a72ea6ce4904a51775afcb5002eae92866 100644 (file)
--- a/gl_draw.c
+++ b/gl_draw.c
@@ -61,6 +61,7 @@ cvar_t r_font_postprocess_shadow_y = {CF_CLIENT | CF_ARCHIVE, "r_font_postproces
 cvar_t r_font_postprocess_shadow_z = {CF_CLIENT | CF_ARCHIVE, "r_font_postprocess_shadow_z", "0", "font shadow Z shift amount, applied during blurring"};
 cvar_t r_font_hinting = {CF_CLIENT | CF_ARCHIVE, "r_font_hinting", "3", "0 = no hinting, 1 = light autohinting, 2 = full autohinting, 3 = full hinting"};
 cvar_t r_font_antialias = {CF_CLIENT | CF_ARCHIVE, "r_font_antialias", "1", "0 = monochrome, 1 = grey" /* , 2 = rgb, 3 = bgr" */};
+cvar_t r_font_always_reload = {CF_CLIENT | CF_ARCHIVE, "r_font_always_reload", "0", "reload a font even given the same loadfont command. useful for trying out different versions of the same font file"};
 cvar_t r_nearest_2d = {CF_CLIENT | CF_ARCHIVE, "r_nearest_2d", "0", "use nearest filtering on all 2d textures (including conchars)"};
 cvar_t r_nearest_conchars = {CF_CLIENT | CF_ARCHIVE, "r_nearest_conchars", "0", "use nearest filtering on conchars texture"};
 
@@ -142,7 +143,7 @@ cachepic_t *Draw_CachePic_Flags(const char *path, unsigned int cachepicflags)
        Con_DPrintf("Draw_CachePic(\"%s\"): frame %i: loading pic%s\n", path, draw_frame, (cachepicflags & CACHEPICFLAG_NOTPERSISTENT) ? " notpersist" : "");
        pic = cachepics + (numcachepics++);
        memset(pic, 0, sizeof(*pic));
-       strlcpy (pic->name, path, sizeof(pic->name));
+       dp_strlcpy (pic->name, path, sizeof(pic->name));
        // link into list
        pic->chain = cachepichash[hashkey];
        cachepichash[hashkey] = pic;
@@ -285,7 +286,7 @@ cachepic_t *Draw_NewPic(const char *picname, int width, int height, unsigned cha
                Con_DPrintf("Draw_NewPic(\"%s\"): frame %i: creating new cachepic\n", picname, draw_frame);
                pic = cachepics + (numcachepics++);
                memset(pic, 0, sizeof(*pic));
-               strlcpy (pic->name, picname, sizeof(pic->name));
+               dp_strlcpy (pic->name, picname, sizeof(pic->name));
                // link into list
                pic->chain = cachepichash[hashkey];
                cachepichash[hashkey] = pic;
@@ -335,7 +336,7 @@ void LoadFont(qbool override, const char *name, dp_font_t *fnt, float scale, flo
 
        if(override || !fnt->texpath[0])
        {
-               strlcpy(fnt->texpath, name, sizeof(fnt->texpath));
+               dp_strlcpy(fnt->texpath, name, sizeof(fnt->texpath));
                // load the cvars when the font is FIRST loader
                fnt->settings.scale = scale;
                fnt->settings.voffset = voffset;
@@ -347,6 +348,7 @@ void LoadFont(qbool override, const char *name, dp_font_t *fnt, float scale, flo
                fnt->settings.shadowy = r_font_postprocess_shadow_y.value;
                fnt->settings.shadowz = r_font_postprocess_shadow_z.value;
        }
+
        // fix bad scale
        if (fnt->settings.scale <= 0)
                fnt->settings.scale = 1;
@@ -356,7 +358,7 @@ void LoadFont(qbool override, const char *name, dp_font_t *fnt, float scale, flo
 
        if(fnt->ft2)
        {
-               // clear freetype font
+               // we are going to reload. clear old ft2 data
                Font_UnloadFont(fnt->ft2);
                Mem_Free(fnt->ft2);
                fnt->ft2 = NULL;
@@ -382,7 +384,7 @@ void LoadFont(qbool override, const char *name, dp_font_t *fnt, float scale, flo
                if(!Draw_IsPicLoaded(fnt->pic))
                {
                        fnt->pic = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION | (r_nearest_conchars.integer ? CACHEPICFLAG_NEAREST : 0));
-                       strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
+                       dp_strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
                }
                else
                        dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->fallbacks[i]);
@@ -458,7 +460,7 @@ void LoadFont(qbool override, const char *name, dp_font_t *fnt, float scale, flo
                        if (!map)
                                break;
                        for(ch = 0; ch < 256; ++ch)
-                               map->width_of[ch] = Font_SnapTo(fnt->width_of[ch], 1/map->size);
+                               fnt->width_of_ft2[i][ch] = Font_SnapTo(fnt->width_of[ch], 1/map->size);
                }
        }
 
@@ -491,7 +493,7 @@ dp_font_t *FindFont(const char *title, qbool allocate_new)
                {
                        if(!strcmp(dp_fonts.f[i].title, ""))
                        {
-                               strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title));
+                               dp_strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title));
                                return &dp_fonts.f[i];
                        }
                }
@@ -506,7 +508,7 @@ dp_font_t *FindFont(const char *title, qbool allocate_new)
                        if (dp_fonts.f[i].ft2)
                                dp_fonts.f[i].ft2->settings = &dp_fonts.f[i].settings;
                // register a font in first expanded slot
-               strlcpy(dp_fonts.f[oldsize].title, title, sizeof(dp_fonts.f[oldsize].title));
+               dp_strlcpy(dp_fonts.f[oldsize].title, title, sizeof(dp_fonts.f[oldsize].title));
                return &dp_fonts.f[oldsize];
        }
        return NULL;
@@ -572,6 +574,16 @@ static void LoadFont_f(cmd_state_t *cmd)
                Con_Printf("font function not found\n");
                return;
        }
+       else
+       {
+               if (strcmp(cmd->cmdline, f->cmdline) != 0 || r_font_always_reload.integer)
+                       dp_strlcpy(f->cmdline, cmd->cmdline, MAX_FONT_CMDLINE);
+               else
+               {
+                       Con_DPrintf("LoadFont: font %s is unchanged\n", Cmd_Argv(cmd, 1));
+                       return;
+               }
+       }
 
        if(Cmd_Argc(cmd) < 3)
                filelist = "gfx/conchars";
@@ -592,8 +604,8 @@ static void LoadFont_f(cmd_state_t *cmd)
                c = cm;
        }
 
-       if(!c || (c - filelist) > MAX_QPATH)
-               strlcpy(mainfont, filelist, sizeof(mainfont));
+       if(!c || (c - filelist) >= MAX_QPATH)
+               dp_strlcpy(mainfont, filelist, sizeof(mainfont));
        else
        {
                memcpy(mainfont, filelist, c - filelist);
@@ -617,9 +629,9 @@ static void LoadFont_f(cmd_state_t *cmd)
                        f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index
                        c = cm;
                }
-               if(!c || (c-filelist) > MAX_QPATH)
+               if(!c || (c-filelist) >= MAX_QPATH)
                {
-                       strlcpy(f->fallbacks[i], filelist, sizeof(mainfont));
+                       dp_strlcpy(f->fallbacks[i], filelist, sizeof(mainfont));
                }
                else
                {
@@ -742,6 +754,7 @@ void GL_Draw_Init (void)
        Cvar_RegisterVariable(&r_font_postprocess_shadow_z);
        Cvar_RegisterVariable(&r_font_hinting);
        Cvar_RegisterVariable(&r_font_antialias);
+       Cvar_RegisterVariable(&r_font_always_reload);
        Cvar_RegisterVariable(&r_textshadow);
        Cvar_RegisterVariable(&r_textbrightness);
        Cvar_RegisterVariable(&r_textcontrast);
@@ -755,15 +768,15 @@ void GL_Draw_Init (void)
        memset(dp_fonts.f, 0, sizeof(dp_font_t) * dp_fonts.maxsize);
 
        // assign starting font names
-       strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
-       strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
-       strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
-       strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
-       strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
-       strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
-       strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
-       strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
-       strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
+       dp_strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
+       dp_strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
+       dp_strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
+       dp_strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
+       dp_strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
+       dp_strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
+       dp_strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
+       dp_strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
+       dp_strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
        for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
                if(!FONT_USER(i)->title[0])
                        dpsnprintf(FONT_USER(i)->title, sizeof(FONT_USER(i)->title), "user%d", j++);
@@ -923,7 +936,6 @@ float DrawQ_TextWidth_UntilWidth_TrackColors_Scale(const char *text, size_t *max
        size_t bytes_left;
        ft2_font_map_t *fontmap = NULL;
        ft2_font_map_t *map = NULL;
-       //ft2_font_map_t *prevmap = NULL;
        ft2_font_t *ft2 = fnt->ft2;
        // float ftbase_x;
        qbool snap = true;
@@ -975,7 +987,7 @@ float DrawQ_TextWidth_UntilWidth_TrackColors_Scale(const char *text, size_t *max
        //      x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway
 
        if (fontmap)
-               width_of = fontmap->width_of;
+               width_of = fnt->width_of_ft2[map_index];
        else
                width_of = fnt->width_of;
 
@@ -990,11 +1002,11 @@ float DrawQ_TextWidth_UntilWidth_TrackColors_Scale(const char *text, size_t *max
                if (ch == ' ' && !fontmap)
                {
                        if(!least_one || i0) // never skip the first character
-                       if(x + width_of[(int) ' '] * dw > maxwidth)
-                       {
-                               i = i0;
-                               break; // oops, can't draw this
-                       }
+                               if(x + width_of[(int) ' '] * dw > maxwidth)
+                               {
+                                       i = i0;
+                                       break; // oops, can't draw this
+                               }
                        x += width_of[(int) ' '] * dw;
                        continue;
                }
@@ -1039,29 +1051,22 @@ float DrawQ_TextWidth_UntilWidth_TrackColors_Scale(const char *text, size_t *max
                                map = ft2_oldstyle_map;
                        prevch = 0;
                        if(!least_one || i0) // never skip the first character
-                       if(x + width_of[ch] * dw > maxwidth)
-                       {
-                               i = i0;
-                               break; // oops, can't draw this
-                       }
+                               if(x + width_of[ch] * dw > maxwidth)
+                               {
+                                       i = i0;
+                                       break; // oops, can't draw this
+                               }
                        x += width_of[ch] * dw;
                } else {
-                       if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
+                       if (!map || map == ft2_oldstyle_map || ch != prevch)
                        {
-                               map = FontMap_FindForChar(fontmap, ch);
+                               Font_GetMapForChar(ft2, map_index, ch, &map, &mapch);
                                if (!map)
-                               {
-                                       if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
-                                               break;
-                                       if (!map)
-                                               break;
-                               }
+                                       break;
                        }
-                       mapch = ch - map->start;
                        if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
                                x += kx * dw;
                        x += map->glyphs[mapch].advance_x * dw;
-                       //prevmap = map;
                        prevch = ch;
                }
        }
@@ -1083,7 +1088,6 @@ float DrawQ_String_Scale(float startx, float starty, const char *text, size_t ma
        Uchar ch, mapch, nextch;
        Uchar prevch = 0; // used for kerning
        int map_index = 0;
-       //ft2_font_map_t *prevmap = NULL; // the previous map
        ft2_font_map_t *map = NULL;     // the currently used map
        ft2_font_map_t *fontmap = NULL; // the font map for the size
        float ftbase_y;
@@ -1146,7 +1150,7 @@ float DrawQ_String_Scale(float startx, float starty, const char *text, size_t ma
        pix_y = vid.height / vid_conheight.value;
 
        if (fontmap)
-               width_of = fontmap->width_of;
+               width_of = fnt->width_of_ft2[map_index];
        else
                width_of = fnt->width_of;
 
@@ -1259,27 +1263,16 @@ float DrawQ_String_Scale(float startx, float starty, const char *text, size_t ma
                                Mod_Mesh_AddTriangle(mod, surf, e0, e2, e3);
                                x += width_of[ch] * dw;
                        } else {
-                               if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
+                               if (!map || map == ft2_oldstyle_map || ch != prevch)
                                {
-                                       // find the new map
-                                       map = FontMap_FindForChar(fontmap, ch);
+                                       Font_GetMapForChar(ft2, map_index, ch, &map, &mapch);
                                        if (!map)
                                        {
-                                               if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
-                                               {
-                                                       shadow = -1;
-                                                       break;
-                                               }
-                                               if (!map)
-                                               {
-                                                       // this shouldn't happen
-                                                       shadow = -1;
-                                                       break;
-                                               }
+                                               shadow = -1;
+                                               break;
                                        }
                                }
 
-                               mapch = ch - map->start;
                                thisw = map->glyphs[mapch].advance_x;
 
                                //x += ftbase_x;
index d164550d78b37c7f6c266d90ddeb29254df53a48..a8a92dd1b6fa63ec91ad7491417689ea105b5df3 100644 (file)
@@ -82,7 +82,7 @@ cvar_t r_transparent_useplanardistance = {CF_CLIENT, "r_transparent_useplanardis
 cvar_t r_showoverdraw = {CF_CLIENT, "r_showoverdraw", "0", "shows overlapping geometry"};
 cvar_t r_showbboxes = {CF_CLIENT, "r_showbboxes", "0", "shows bounding boxes of server entities, value controls opacity scaling (1 = 10%,  10 = 100%)"};
 cvar_t r_showbboxes_client = {CF_CLIENT, "r_showbboxes_client", "0", "shows bounding boxes of clientside qc entities, value controls opacity scaling (1 = 10%,  10 = 100%)"};
-cvar_t r_showsurfaces = {CF_CLIENT, "r_showsurfaces", "0", "1 shows surfaces as different colors, or a value of 2 shows triangle draw order (for analyzing whether meshes are optimized for vertex cache)"};
+cvar_t r_showsurfaces = {CF_CLIENT, "r_showsurfaces", "0", "1 shows surfaces as different colors, or a value of 3 shows an approximation to vertex or object color (for a very approximate view of the game)"};
 cvar_t r_showtris = {CF_CLIENT, "r_showtris", "0", "shows triangle outlines, value controls brightness (can be above 1)"};
 cvar_t r_shownormals = {CF_CLIENT, "r_shownormals", "0", "shows per-vertex surface normals and tangent vectors for bumpmapped lighting"};
 cvar_t r_showlighting = {CF_CLIENT, "r_showlighting", "0", "shows areas lit by lights, useful for finding out why some areas of a map render slowly (bright orange = lots of passes = slow), a value of 2 disables depth testing which can be interesting but not very useful"};
@@ -156,12 +156,8 @@ cvar_t gl_skyclip = {CF_CLIENT, "gl_skyclip", "4608", "nehahra farclip distance
 cvar_t r_texture_dds_load = {CF_CLIENT | CF_ARCHIVE, "r_texture_dds_load", "0", "load compressed dds/filename.dds texture instead of filename.tga, if the file exists (requires driver support)"};
 cvar_t r_texture_dds_save = {CF_CLIENT | CF_ARCHIVE, "r_texture_dds_save", "0", "save compressed dds/filename.dds texture when filename.tga is loaded, so that it can be loaded instead next time"};
 
-cvar_t r_textureunits = {CF_CLIENT, "r_textureunits", "32", "number of texture units to use in GL 1.1 and GL 1.3 rendering paths"};
-static cvar_t gl_combine = {CF_CLIENT | CF_READONLY, "gl_combine", "1", "indicates whether the OpenGL 1.3 rendering path is active"};
-static cvar_t r_glsl = {CF_CLIENT | CF_READONLY, "r_glsl", "1", "indicates whether the OpenGL 2.0 rendering path is active"};
-
 cvar_t r_usedepthtextures = {CF_CLIENT | CF_ARCHIVE, "r_usedepthtextures", "1", "use depth texture instead of depth renderbuffer where possible, uses less video memory but may render slower (or faster) depending on hardware"};
-cvar_t r_viewfbo = {CF_CLIENT | CF_ARCHIVE, "r_viewfbo", "0", "enables use of an 8bit (1) or 16bit (2) or 32bit (3) per component float framebuffer render, which may be at a different resolution than the video mode"};
+cvar_t r_viewfbo = {CF_CLIENT | CF_ARCHIVE, "r_viewfbo", "0", "enables use of an 8bit (1) or 16bit (2) or 32bit (3) per component float framebuffer render, which may be at a different resolution than the video mode; the default setting of 0 uses a framebuffer render when required, and renders directly to the screen otherwise"};
 cvar_t r_rendertarget_debug = {CF_CLIENT, "r_rendertarget_debug", "-1", "replaces the view with the contents of the specified render target (by number - note that these can fluctuate depending on scene)"};
 cvar_t r_viewscale = {CF_CLIENT | CF_ARCHIVE, "r_viewscale", "1", "scaling factor for resolution of the fbo rendering method, must be > 0, can be above 1 for a costly antialiasing behavior, typical values are 0.5 for 1/4th as many pixels rendered, or 1 for normal rendering"};
 cvar_t r_viewscale_fpsscaling = {CF_CLIENT | CF_ARCHIVE, "r_viewscale_fpsscaling", "0", "change resolution based on framerate"};
@@ -191,6 +187,7 @@ cvar_t r_glsl_postprocess_uservec2_enable = {CF_CLIENT | CF_ARCHIVE, "r_glsl_pos
 cvar_t r_glsl_postprocess_uservec3_enable = {CF_CLIENT | CF_ARCHIVE, "r_glsl_postprocess_uservec3_enable", "1", "enables postprocessing uservec3 usage, creates USERVEC1 define (only useful if default.glsl has been customized)"};
 cvar_t r_glsl_postprocess_uservec4_enable = {CF_CLIENT | CF_ARCHIVE, "r_glsl_postprocess_uservec4_enable", "1", "enables postprocessing uservec4 usage, creates USERVEC1 define (only useful if default.glsl has been customized)"};
 cvar_t r_colorfringe = {CF_CLIENT | CF_ARCHIVE, "r_colorfringe", "0", "Chromatic aberration. Values higher than 0.025 will noticeably distort the image"};
+cvar_t r_fxaa = {CF_CLIENT | CF_ARCHIVE, "r_fxaa", "0", "fast approximate anti aliasing"};
 
 cvar_t r_water = {CF_CLIENT | CF_ARCHIVE, "r_water", "0", "whether to use reflections and refraction on water surfaces (note: r_wateralpha must be set below 1)"};
 cvar_t r_water_cameraentitiesonly = {CF_CLIENT | CF_ARCHIVE, "r_water_cameraentitiesonly", "0", "whether to only show QC-defined reflections/refractions (typically used for camera- or portal-like effects)"};
@@ -257,7 +254,7 @@ cvar_t r_buffermegs[R_BUFFERDATA_COUNT] =
        {CF_CLIENT | CF_ARCHIVE, "r_buffermegs_uniform", "0.25", "uniform buffer size for one frame"},
 };
 
-cvar_t r_q1bsp_lightmap_updates_enabled = {CF_CLIENT | CF_ARCHIVE, "r_q1bsp_lightmap_updates_enabled", "1", "allow lightmaps to be updated on Q1BSP maps (don't turn this off except for debugging)"};
+cvar_t r_q1bsp_lightmap_updates_enabled = {CF_CLIENT, "r_q1bsp_lightmap_updates_enabled", "1", "allow lightmaps to be updated on Q1BSP maps (don't turn this off except for debugging)"};
 cvar_t r_q1bsp_lightmap_updates_combine = {CF_CLIENT | CF_ARCHIVE, "r_q1bsp_lightmap_updates_combine", "2", "combine lightmap texture updates to make fewer glTexSubImage2D calls, modes: 0 = immediately upload lightmaps (may be thousands of small 3x3 updates), 1 = combine to one call, 2 = combine to one full texture update (glTexImage2D) which tells the driver it does not need to lock the resource (faster on most drivers)"};
 cvar_t r_q1bsp_lightmap_updates_hidden_surfaces = {CF_CLIENT | CF_ARCHIVE, "r_q1bsp_lightmap_updates_hidden_surfaces", "0", "update lightmaps on surfaces that are not visible, so that updates only occur on frames where lightstyles changed value (animation or light switches), only makes sense with combine = 2"};
 
@@ -536,7 +533,7 @@ static void R_BuildFogHeightTexture(void)
        float c[4];
        float f;
        inpixels = NULL;
-       strlcpy(r_refdef.fogheighttexturename, r_refdef.fog_height_texturename, sizeof(r_refdef.fogheighttexturename));
+       dp_strlcpy(r_refdef.fogheighttexturename, r_refdef.fog_height_texturename, sizeof(r_refdef.fogheighttexturename));
        if (r_refdef.fogheighttexturename[0])
                inpixels = loadimagepixelsbgra(r_refdef.fogheighttexturename, true, false, false, NULL);
        if (!inpixels)
@@ -1026,7 +1023,7 @@ static char *ShaderModeInfo_GetShaderText(shadermodeinfo_t *modeinfo, qbool prin
 
 static void R_GLSL_CompilePermutation(r_glsl_permutation_t *p, unsigned int mode, uint64_t permutation)
 {
-       int i;
+       unsigned i;
        int ubibind;
        int sampler;
        shadermodeinfo_t *modeinfo = &shadermodeinfo[SHADERLANGUAGE_GLSL][mode];
@@ -1047,7 +1044,7 @@ static void R_GLSL_CompilePermutation(r_glsl_permutation_t *p, unsigned int mode
        permutationname[0] = 0;
        sourcestring = ShaderModeInfo_GetShaderText(modeinfo, true, false);
 
-       strlcat(permutationname, modeinfo->filename, sizeof(permutationname));
+       dp_strlcat(permutationname, modeinfo->filename, sizeof(permutationname));
 
        // we need 140 for r_glsl_skeletal (GL_ARB_uniform_buffer_object)
        if(vid.support.glshaderversion >= 140)
@@ -1101,7 +1098,7 @@ static void R_GLSL_CompilePermutation(r_glsl_permutation_t *p, unsigned int mode
        vertstrings_list[vertstrings_count++] = modeinfo->pretext;
        geomstrings_list[geomstrings_count++] = modeinfo->pretext;
        fragstrings_list[fragstrings_count++] = modeinfo->pretext;
-       strlcat(permutationname, modeinfo->name, sizeof(permutationname));
+       dp_strlcat(permutationname, modeinfo->name, sizeof(permutationname));
 
        // now add all the permutation pretexts
        for (i = 0;i < SHADERPERMUTATION_COUNT;i++)
@@ -1111,7 +1108,7 @@ static void R_GLSL_CompilePermutation(r_glsl_permutation_t *p, unsigned int mode
                        vertstrings_list[vertstrings_count++] = shaderpermutationinfo[i].pretext;
                        geomstrings_list[geomstrings_count++] = shaderpermutationinfo[i].pretext;
                        fragstrings_list[fragstrings_count++] = shaderpermutationinfo[i].pretext;
-                       strlcat(permutationname, shaderpermutationinfo[i].name, sizeof(permutationname));
+                       dp_strlcat(permutationname, shaderpermutationinfo[i].name, sizeof(permutationname));
                }
                else
                {
@@ -1362,7 +1359,7 @@ static void R_SetupShader_SetPermutationGLSL(unsigned int mode, uint64_t permuta
                        if (!r_glsl_permutation->program)
                        {
                                // remove features until we find a valid permutation
-                               int i;
+                               unsigned i;
                                for (i = 0;i < SHADERPERMUTATION_COUNT;i++)
                                {
                                        // reduce i more quickly whenever it would not remove any bits
@@ -1421,7 +1418,8 @@ void R_GLSL_Restart_f(cmd_state_t *cmd)
 
 static void R_GLSL_DumpShader_f(cmd_state_t *cmd)
 {
-       int i, language, mode, dupe;
+       unsigned i;
+       int language, mode, dupe;
        char *text;
        shadermodeinfo_t *modeinfo;
        qfile_t *file;
@@ -1925,7 +1923,7 @@ void R_SetupShader_Surface(const float rtlightambient[3], const float rtlightdif
                        if (r_glsl_permutation->loc_Color_Ambient >= 0) qglUniform3f(r_glsl_permutation->loc_Color_Ambient, rtlightambient[0], rtlightambient[1], rtlightambient[2]);
                        if (r_glsl_permutation->loc_Color_Diffuse >= 0) qglUniform3f(r_glsl_permutation->loc_Color_Diffuse, rtlightdiffuse[0], rtlightdiffuse[1], rtlightdiffuse[2]);
                        if (r_glsl_permutation->loc_Color_Specular >= 0) qglUniform3f(r_glsl_permutation->loc_Color_Specular, rtlightspecular[0], rtlightspecular[1], rtlightspecular[2]);
-       
+
                        // additive passes are only darkened by fog, not tinted
                        if (r_glsl_permutation->loc_FogColor >= 0)
                                qglUniform3f(r_glsl_permutation->loc_FogColor, 0, 0, 0);
@@ -2263,7 +2261,7 @@ skinframe_t *R_SkinFrame_Find(const char *name, int textureflags, int comparewid
                        return NULL;
                item = (skinframe_t *)Mem_ExpandableArray_AllocRecord(&r_skinframe.array);
                memset(item, 0, sizeof(*item));
-               strlcpy(item->basename, basename, sizeof(item->basename));
+               dp_strlcpy(item->basename, basename, sizeof(item->basename));
                item->textureflags = compareflags;
                item->comparewidth = comparewidth;
                item->compareheight = compareheight;
@@ -2992,7 +2990,7 @@ rtexture_t *R_GetCubemap(const char *basename)
                return r_texture_whitecube;
        r_texture_numcubemaps++;
        r_texture_cubemaps[i] = (cubemapinfo_t *)Mem_Alloc(r_main_mempool, sizeof(cubemapinfo_t));
-       strlcpy(r_texture_cubemaps[i]->basename, basename, sizeof(r_texture_cubemaps[i]->basename));
+       dp_strlcpy(r_texture_cubemaps[i]->basename, basename, sizeof(r_texture_cubemaps[i]->basename));
        r_texture_cubemaps[i]->texture = R_LoadCubemap(r_texture_cubemaps[i]->basename);
        return r_texture_cubemaps[i]->texture;
 }
@@ -3071,9 +3069,6 @@ static void gl_main_start(void)
        {
        case RENDERPATH_GL32:
        case RENDERPATH_GLES2:
-               Cvar_SetValueQuick(&r_textureunits, MAX_TEXTUREUNITS);
-               Cvar_SetValueQuick(&gl_combine, 1);
-               Cvar_SetValueQuick(&r_glsl, 1);
                r_loadnormalmap = true;
                r_loadgloss = true;
                r_loadfog = false;
@@ -3335,8 +3330,6 @@ void GL_Main_Init(void)
        Cvar_RegisterVariable(&r_transparent_sortarraysize);
        Cvar_RegisterVariable(&r_texture_dds_load);
        Cvar_RegisterVariable(&r_texture_dds_save);
-       Cvar_RegisterVariable(&r_textureunits);
-       Cvar_RegisterVariable(&gl_combine);
        Cvar_RegisterVariable(&r_usedepthtextures);
        Cvar_RegisterVariable(&r_viewfbo);
        Cvar_RegisterVariable(&r_rendertarget_debug);
@@ -3347,7 +3340,6 @@ void GL_Main_Init(void)
        Cvar_RegisterVariable(&r_viewscale_fpsscaling_stepsize);
        Cvar_RegisterVariable(&r_viewscale_fpsscaling_stepmax);
        Cvar_RegisterVariable(&r_viewscale_fpsscaling_target);
-       Cvar_RegisterVariable(&r_glsl);
        Cvar_RegisterVariable(&r_glsl_deluxemapping);
        Cvar_RegisterVariable(&r_glsl_offsetmapping);
        Cvar_RegisterVariable(&r_glsl_offsetmapping_steps);
@@ -3368,6 +3360,7 @@ void GL_Main_Init(void)
        Cvar_RegisterVariable(&r_glsl_postprocess_uservec4_enable);
        Cvar_RegisterVariable(&r_celshading);
        Cvar_RegisterVariable(&r_celoutlines);
+       Cvar_RegisterVariable(&r_fxaa);
 
        Cvar_RegisterVariable(&r_water);
        Cvar_RegisterVariable(&r_water_cameraentitiesonly);
@@ -3447,6 +3440,7 @@ void Render_Init(void)
        R_Particles_Init();
        R_Explosion_Init();
        R_LightningBeams_Init();
+       CL_MeshEntities_Init();
        Mod_RenderInit();
 }
 
@@ -3835,7 +3829,7 @@ qbool R_AnimCache_GetEntity(entity_render_t *ent, qbool wantnormals, qbool wantt
                r_refdef.stats[r_stat_animcache_skeletal_bones] += model->num_bones;
                r_refdef.stats[r_stat_animcache_skeletal_maxbones] = max(r_refdef.stats[r_stat_animcache_skeletal_maxbones], model->num_bones);
                ent->animcache_skeletaltransform3x4 = (float *)R_FrameData_Alloc(sizeof(float[3][4]) * model->num_bones);
-               Mod_Skeletal_BuildTransforms(model, ent->frameblend, ent->skeleton, NULL, ent->animcache_skeletaltransform3x4); 
+               Mod_Skeletal_BuildTransforms(model, ent->frameblend, ent->skeleton, NULL, ent->animcache_skeletaltransform3x4);
                // note: this can fail if the buffer is at the grow limit
                ent->animcache_skeletaltransform3x4size = sizeof(float[3][4]) * model->num_bones;
                ent->animcache_skeletaltransform3x4buffer = R_BufferData_Store(ent->animcache_skeletaltransform3x4size, ent->animcache_skeletaltransform3x4, R_BUFFERDATA_UNIFORM, &ent->animcache_skeletaltransform3x4offset);
@@ -3959,13 +3953,13 @@ qbool R_CanSeeBox(int numsamples, vec_t eyejitter, vec_t entboxenlarge, vec_t en
        if (BoxesOverlap(boxmins, boxmaxs, eyemins, eyemaxs))
                return true;
 
+       VectorCopy(eye, start);
        // try specific positions in the box first - note that these can be cached
        if (r_cullentities_trace_entityocclusion.integer)
        {
                for (i = 0; i < sizeof(positions) / sizeof(positions[0]); i++)
                {
                        trace_t trace;
-                       VectorCopy(eye, start);
                        end[0] = boxmins[0] + (boxmaxs[0] - boxmins[0]) * positions[i][0];
                        end[1] = boxmins[1] + (boxmaxs[1] - boxmins[1]) * positions[i][1];
                        end[2] = boxmins[2] + (boxmaxs[2] - boxmins[2]) * positions[i][2];
@@ -3976,8 +3970,13 @@ qbool R_CanSeeBox(int numsamples, vec_t eyejitter, vec_t entboxenlarge, vec_t en
                                return true;
                }
        }
-       else if (model->brush.TraceLineOfSight(model, start, end, padmins, padmaxs))
-               return true;
+       else
+       {
+               // try center
+               VectorMAM(0.5f, boxmins, 0.5f, boxmaxs, end);
+               if (model->brush.TraceLineOfSight(model, start, end, padmins, padmaxs))
+                       return true;
+       }
 
        // try various random positions
        for (j = 0; j < numsamples; j++)
@@ -4381,7 +4380,7 @@ static void R_View_SetFrustum(const int *scissor)
        //PlaneClassify(&frustum[4]);
 }
 
-static void R_View_UpdateWithScissor(const int *myscissor)
+static void R_View_Update(const int *myscissor)
 {
        R_Main_ResizeViewCache();
        R_View_SetFrustum(myscissor);
@@ -4389,21 +4388,13 @@ static void R_View_UpdateWithScissor(const int *myscissor)
        R_View_UpdateEntityVisible();
 }
 
-static void R_View_Update(void)
-{
-       R_Main_ResizeViewCache();
-       R_View_SetFrustum(NULL);
-       R_View_WorldVisibility(!r_refdef.view.usevieworiginculling);
-       R_View_UpdateEntityVisible();
-}
-
 float viewscalefpsadjusted = 1.0f;
 
 void R_SetupView(qbool allowwaterclippingplane, int viewfbo, rtexture_t *viewdepthtexture, rtexture_t *viewcolortexture, int viewx, int viewy, int viewwidth, int viewheight)
 {
        const float *customclipplane = NULL;
        float plane[4];
-       int /*rtwidth,*/ rtheight;
+       int viewy_adjusted;
        if (r_refdef.view.useclipplane && allowwaterclippingplane)
        {
                // LadyHavoc: couldn't figure out how to make this approach work the same in DPSOFTRAST
@@ -4418,15 +4409,16 @@ void R_SetupView(qbool allowwaterclippingplane, int viewfbo, rtexture_t *viewdep
                customclipplane = plane;
        }
 
-       //rtwidth = viewfbo ? R_TextureWidth(viewdepthtexture ? viewdepthtexture : viewcolortexture) : vid.width;
-       rtheight = viewfbo ? R_TextureHeight(viewdepthtexture ? viewdepthtexture : viewcolortexture) : vid.height;
+       // GL is weird because it's bottom to top, r_refdef.view.y is top to bottom.
+       // Unless the render target is a FBO...
+       viewy_adjusted = viewfbo ? viewy : vid.height - viewheight - viewy;
 
        if (!r_refdef.view.useperspective)
-               R_Viewport_InitOrtho3D(&r_refdef.view.viewport, &r_refdef.view.matrix, viewx, rtheight - viewheight - viewy, viewwidth, viewheight, r_refdef.view.ortho_x, r_refdef.view.ortho_y, -r_refdef.farclip, r_refdef.farclip, customclipplane);
+               R_Viewport_InitOrtho3D(&r_refdef.view.viewport, &r_refdef.view.matrix, viewx, viewy_adjusted, viewwidth, viewheight, r_refdef.view.ortho_x, r_refdef.view.ortho_y, -r_refdef.farclip, r_refdef.farclip, customclipplane);
        else if (vid.stencil && r_useinfinitefarclip.integer)
-               R_Viewport_InitPerspectiveInfinite(&r_refdef.view.viewport, &r_refdef.view.matrix, viewx, rtheight - viewheight - viewy, viewwidth, viewheight, r_refdef.view.frustum_x, r_refdef.view.frustum_y, r_refdef.nearclip, customclipplane);
+               R_Viewport_InitPerspectiveInfinite(&r_refdef.view.viewport, &r_refdef.view.matrix, viewx, viewy_adjusted, viewwidth, viewheight, r_refdef.view.frustum_x, r_refdef.view.frustum_y, r_refdef.nearclip, customclipplane);
        else
-               R_Viewport_InitPerspective(&r_refdef.view.viewport, &r_refdef.view.matrix, viewx, rtheight - viewheight - viewy, viewwidth, viewheight, r_refdef.view.frustum_x, r_refdef.view.frustum_y, r_refdef.nearclip, r_refdef.farclip, customclipplane);
+               R_Viewport_InitPerspective(&r_refdef.view.viewport, &r_refdef.view.matrix, viewx, viewy_adjusted, viewwidth, viewheight, r_refdef.view.frustum_x, r_refdef.view.frustum_y, r_refdef.nearclip, r_refdef.farclip, customclipplane);
        R_Mesh_SetRenderTargets(viewfbo, viewdepthtexture, viewcolortexture, NULL, NULL, NULL);
        R_SetViewport(&r_refdef.view.viewport);
 }
@@ -4456,11 +4448,15 @@ void R_EntityMatrix(const matrix4x4_t *matrix)
 void R_ResetViewRendering2D_Common(int viewfbo, rtexture_t *viewdepthtexture, rtexture_t *viewcolortexture, int viewx, int viewy, int viewwidth, int viewheight, float x2, float y2)
 {
        r_viewport_t viewport;
+       int viewy_adjusted;
 
        CHECKGLERROR
 
-       // GL is weird because it's bottom to top, r_refdef.view.y is top to bottom
-       R_Viewport_InitOrtho(&viewport, &identitymatrix, viewx, vid.height - viewheight - viewy, viewwidth, viewheight, 0, 0, x2, y2, -10, 100, NULL);
+       // GL is weird because it's bottom to top, r_refdef.view.y is top to bottom.
+       // Unless the render target is a FBO...
+       viewy_adjusted = viewfbo ? viewy : vid.height - viewheight - viewy;
+
+       R_Viewport_InitOrtho(&viewport, &identitymatrix, viewx, viewy_adjusted, viewwidth, viewheight, 0, 0, x2, y2, -10, 100, NULL);
        R_Mesh_SetRenderTargets(viewfbo, viewdepthtexture, viewcolortexture, NULL, NULL, NULL);
        R_SetViewport(&viewport);
        GL_Scissor(viewport.x, viewport.y, viewport.width, viewport.height);
@@ -4864,10 +4860,7 @@ static void R_Water_ProcessPlanes(int fbo, rtexture_t *depthtexture, rtexture_t
                        GL_ScissorTest(false);
                        R_ClearScreen(r_refdef.fogenabled);
                        GL_ScissorTest(true);
-                       if(r_water_scissormode.integer & 2)
-                               R_View_UpdateWithScissor(myscissor);
-                       else
-                               R_View_Update();
+                       R_View_Update(r_water_scissormode.integer & 2 ? myscissor : NULL);
                        R_AnimCache_CacheVisibleEntities();
                        if(r_water_scissormode.integer & 1)
                                GL_Scissor(myscissor[0], myscissor[1], myscissor[2], myscissor[3]);
@@ -4932,10 +4925,7 @@ static void R_Water_ProcessPlanes(int fbo, rtexture_t *depthtexture, rtexture_t
                        GL_ScissorTest(false);
                        R_ClearScreen(r_refdef.fogenabled);
                        GL_ScissorTest(true);
-                       if(r_water_scissormode.integer & 2)
-                               R_View_UpdateWithScissor(myscissor);
-                       else
-                               R_View_Update();
+                       R_View_Update(r_water_scissormode.integer & 2 ? myscissor : NULL);
                        R_AnimCache_CacheVisibleEntities();
                        if(r_water_scissormode.integer & 1)
                                GL_Scissor(myscissor[0], myscissor[1], myscissor[2], myscissor[3]);
@@ -4982,7 +4972,7 @@ static void R_Water_ProcessPlanes(int fbo, rtexture_t *depthtexture, rtexture_t
                                r_refdef.view.usecustompvs = true;
                                r_refdef.scene.worldmodel->brush.FatPVS(r_refdef.scene.worldmodel, visorigin, 2, r_refdef.viewcache.world_pvsbits, (r_refdef.viewcache.world_numclusters+7)>>3, false);
                        }
-                       
+
                        // camera needs no clipplane
                        r_refdef.view.useclipplane = false;
                        // TODO: is the camera origin always valid?  if so we don't need to clear this
@@ -4996,7 +4986,7 @@ static void R_Water_ProcessPlanes(int fbo, rtexture_t *depthtexture, rtexture_t
                        GL_ScissorTest(false);
                        R_ClearScreen(r_refdef.fogenabled);
                        GL_ScissorTest(true);
-                       R_View_Update();
+                       R_View_Update(NULL);
                        R_AnimCache_CacheVisibleEntities();
                        R_RenderScene(rt->fbo, rt->depthtexture, rt->colortexture[0], 0, 0, rt->texturewidth, rt->textureheight);
 
@@ -5008,7 +4998,7 @@ static void R_Water_ProcessPlanes(int fbo, rtexture_t *depthtexture, rtexture_t
        r_fb.water.renderingscene = false;
        r_refdef.view = originalview;
        R_ResetViewRendering3D(fbo, depthtexture, colortexture, viewx, viewy, viewwidth, viewheight);
-       R_View_Update();
+       R_View_Update(NULL);
        R_AnimCache_CacheVisibleEntities();
        goto finish;
 error:
@@ -5240,35 +5230,57 @@ static void R_Bloom_MakeTexture(void)
        r_fb.rt_bloom = cur;
 }
 
-static void R_BlendView(int viewfbo, rtexture_t *viewdepthtexture, rtexture_t *viewcolortexture, int viewx, int viewy, int viewwidth, int viewheight)
+static qbool R_BlendView_IsTrivial(int viewwidth, int viewheight, int width, int height)
 {
-       uint64_t permutation;
-       float uservecs[4][4];
-       rtexture_t *viewtexture;
-       rtexture_t *bloomtexture;
+       // Scaling requested?
+       if (viewwidth != width || viewheight != height)
+               return false;
+       // Higher bit depth or explicit FBO requested?
+       if (r_viewfbo.integer)
+               return false;
+       // Non-trivial postprocessing shader permutation?
+       if (r_fb.bloomwidth
+       || r_refdef.viewblend[3] > 0
+       || !vid_gammatables_trivial
+       || r_glsl_postprocess.integer
+       || ((!R_Stereo_ColorMasking() && r_glsl_saturation.value != 1)))
+               return false;
+       // Other reasons for a non-trivial default postprocessing shader?
+       // (See R_CompileShader_CheckStaticParms but only those relevant for MODE_POSTPROCESS in shader_glsl.h)
+       // Skip: if (r_glsl_saturation_redcompensate.integer) (already covered by saturation above).
+       // Skip: if (r_glsl_postprocess.integer) (already covered by r_glsl_postprocess above).
+       // Skip: if (r_glsl_postprocess_uservec1_enable.integer) (already covered by r_glsl_postprocessing above).
+       if (r_fxaa.integer)
+               return false;
+       if (r_colorfringe.value)
+               return false;
+       return true;
+}
 
+static void R_MotionBlurView(int viewfbo, rtexture_t *viewdepthtexture, rtexture_t *viewcolortexture, int viewx, int viewy, int viewwidth, int viewheight)
+{
        R_EntityMatrix(&identitymatrix);
 
-       if(r_refdef.view.ismain && !R_Stereo_Active() && (r_motionblur.value > 0 || r_damageblur.value > 0) && r_fb.ghosttexture)
+       if(r_refdef.view.ismain && !R_Stereo_Active() && (r_motionblur.value > 0 || (r_damageblur.value > 0 && cl.cshifts[CSHIFT_DAMAGE].percent != 0)) && r_fb.ghosttexture)
        {
                // declare variables
                float blur_factor, blur_mouseaccel, blur_velocity;
-               static float blur_average; 
+               static float blur_average;
                static vec3_t blur_oldangles; // used to see how quickly the mouse is moving
 
                // set a goal for the factoring
-               blur_velocity = bound(0, (VectorLength(cl.movement_velocity) - r_motionblur_velocityfactor_minspeed.value) 
+               blur_velocity = bound(0, (VectorLength(cl.movement_velocity) - r_motionblur_velocityfactor_minspeed.value)
                        / max(1, r_motionblur_velocityfactor_maxspeed.value - r_motionblur_velocityfactor_minspeed.value), 1);
-               blur_mouseaccel = bound(0, ((fabs(VectorLength(cl.viewangles) - VectorLength(blur_oldangles)) * 10) - r_motionblur_mousefactor_minspeed.value) 
+               blur_mouseaccel = bound(0, ((fabs(VectorLength(cl.viewangles) - VectorLength(blur_oldangles)) * 10) - r_motionblur_mousefactor_minspeed.value)
                        / max(1, r_motionblur_mousefactor_maxspeed.value - r_motionblur_mousefactor_minspeed.value), 1);
-               blur_factor = ((blur_velocity * r_motionblur_velocityfactor.value) 
+               blur_factor = ((blur_velocity * r_motionblur_velocityfactor.value)
                        + (blur_mouseaccel * r_motionblur_mousefactor.value));
 
                // from the goal, pick an averaged value between goal and last value
                cl.motionbluralpha = bound(0, (cl.time - cl.oldtime) / max(0.001, r_motionblur_averaging.value), 1);
                blur_average = blur_average * (1 - cl.motionbluralpha) + blur_factor * cl.motionbluralpha;
 
-               // enforce minimum amount of blur 
+               // enforce minimum amount of blur
                blur_factor = blur_average * (1 - r_motionblur_minblur.value) + r_motionblur_minblur.value;
 
                //Con_Printf("motionblur: direct factor: %f, averaged factor: %f, velocity: %f, mouse accel: %f \n", blur_factor, blur_average, blur_velocity, blur_mouseaccel);
@@ -5288,7 +5300,7 @@ static void R_BlendView(int viewfbo, rtexture_t *viewdepthtexture, rtexture_t *v
                cl.motionbluralpha *= lhrandom(1 - r_motionblur_randomize.value, 1 + r_motionblur_randomize.value);
                cl.motionbluralpha = bound(0, cl.motionbluralpha, r_motionblur_maxblur.value);
 
-               // apply the blur
+               // apply the blur on top of the current view
                R_ResetViewRendering2D(viewfbo, viewdepthtexture, viewcolortexture, viewx, viewy, viewwidth, viewheight);
                if (cl.motionbluralpha > 0 && !r_refdef.envmap && r_fb.ghosttexture_valid)
                {
@@ -5309,6 +5321,16 @@ static void R_BlendView(int viewfbo, rtexture_t *viewdepthtexture, rtexture_t *v
                r_refdef.stats[r_stat_bloom_copypixels] += viewwidth * viewheight;
                r_fb.ghosttexture_valid = true;
        }
+}
+
+static void R_BlendView(rtexture_t *viewcolortexture, int fbo, rtexture_t *depthtexture, rtexture_t *colortexture, int x, int y, int width, int height)
+{
+       uint64_t permutation;
+       float uservecs[4][4];
+       rtexture_t *viewtexture;
+       rtexture_t *bloomtexture;
+
+       R_EntityMatrix(&identitymatrix);
 
        if (r_fb.bloomwidth)
        {
@@ -5330,11 +5352,11 @@ static void R_BlendView(int viewfbo, rtexture_t *viewdepthtexture, rtexture_t *v
                sscanf(r_glsl_postprocess_uservec4.string, "%f %f %f %f", &uservecs[3][0], &uservecs[3][1], &uservecs[3][2], &uservecs[3][3]);
 
        // render to the screen fbo
-       R_ResetViewRendering2D(viewfbo, viewdepthtexture, viewcolortexture, viewx, viewy, viewwidth, viewheight);
+       R_ResetViewRendering2D(fbo, depthtexture, colortexture, x, y, width, height);
        GL_Color(1, 1, 1, 1);
        GL_BlendFunc(GL_ONE, GL_ZERO);
 
-       viewtexture = r_fb.rt_screen->colortexture[0];
+       viewtexture = viewcolortexture;
        bloomtexture = r_fb.rt_bloom ? r_fb.rt_bloom->colortexture[0] : NULL;
 
        if (r_rendertarget_debug.integer >= 0)
@@ -5491,6 +5513,11 @@ void R_UpdateVariables(void)
        if (r_refdef.scene.worldmodel)
        {
                r_refdef.scene.lightmapintensity *= r_refdef.scene.worldmodel->lightmapscale;
+
+               // Apply the default lightstyle to the lightmap even on q3bsp
+               if (cl.worldmodel && cl.worldmodel->type == mod_brushq3) {
+                       r_refdef.scene.lightmapintensity *= r_refdef.scene.rtlightstylevalue[0];
+               }
        }
        if (r_showsurfaces.integer)
        {
@@ -5623,6 +5650,7 @@ void R_RenderView(int fbo, rtexture_t *depthtexture, rtexture_t *colortexture, i
        rtexture_t *viewdepthtexture = NULL;
        rtexture_t *viewcolortexture = NULL;
        int viewx = r_refdef.view.x, viewy = r_refdef.view.y, viewwidth = r_refdef.view.width, viewheight = r_refdef.view.height;
+       qbool skipblend;
 
        // finish any 2D rendering that was queued
        DrawQ_Finish();
@@ -5697,9 +5725,21 @@ void R_RenderView(int fbo, rtexture_t *depthtexture, rtexture_t *colortexture, i
        if(r_fb.rt_bloom)
                r_refdef.view.colorscale *= r_bloom_scenebrightness.value;
 
-       // R_Bloom_StartFrame probably set up an fbo for us to render into, it will be rendered to the window later in R_BlendView
-       if (r_fb.rt_screen)
+       skipblend = R_BlendView_IsTrivial(r_fb.rt_screen->texturewidth, r_fb.rt_screen->textureheight, width, height);
+       if (skipblend)
        {
+               // Render to the screen right away.
+               viewfbo = fbo;
+               viewdepthtexture = depthtexture;
+               viewcolortexture = colortexture;
+               viewx = x;
+               viewy = y;
+               viewwidth = width;
+               viewheight = height;
+       }
+       else if (r_fb.rt_screen)
+       {
+               // R_Bloom_StartFrame probably set up an fbo for us to render into, it will be rendered to the window later in R_BlendView
                viewfbo = r_fb.rt_screen->fbo;
                viewdepthtexture = r_fb.rt_screen->depthtexture;
                viewcolortexture = r_fb.rt_screen->colortexture[0];
@@ -5729,7 +5769,7 @@ void R_RenderView(int fbo, rtexture_t *depthtexture, rtexture_t *colortexture, i
 
        r_refdef.view.showdebug = true;
 
-       R_View_Update();
+       R_View_Update(NULL);
        if (r_timereport_active)
                R_TimeReport("visibility");
 
@@ -5748,14 +5788,16 @@ void R_RenderView(int fbo, rtexture_t *depthtexture, rtexture_t *colortexture, i
        // test needs to be on
        if (r_fb.rt_screen)
                GL_ScissorTest(true);
-       GL_Scissor(viewx, viewy, viewwidth, viewheight);
+       GL_Scissor(r_refdef.view.viewport.x, r_refdef.view.viewport.y, r_refdef.view.viewport.width, r_refdef.view.viewport.height);
        R_RenderScene(viewfbo, viewdepthtexture, viewcolortexture, viewx, viewy, viewwidth, viewheight);
        r_fb.water.numwaterplanes = 0;
 
        // postprocess uses textures that are not aligned with the viewport we're rendering, so no scissoring
        GL_ScissorTest(false);
 
-       R_BlendView(fbo, depthtexture, colortexture, x, y, width, height);
+       R_MotionBlurView(viewfbo, viewdepthtexture, viewcolortexture, viewx, viewy, viewwidth, viewheight);
+       if (!skipblend)
+               R_BlendView(viewcolortexture, fbo, depthtexture, colortexture, x, y, width, height);
        if (r_timereport_active)
                R_TimeReport("blendview");
 
@@ -6005,7 +6047,7 @@ static const unsigned short bboxelements[36] =
 };
 
 #define BBOXEDGES 13
-static const float bboxedges[BBOXEDGES][6] = 
+static const float bboxedges[BBOXEDGES][6] =
 {
        // whole box
        { 0, 0, 0, 1, 1, 1 },
@@ -6520,7 +6562,7 @@ static void R_LoadQWSkin(r_qwskincache_t *cache, const char *skinname)
        char name[MAX_QPATH];
        skinframe_t *skinframe;
        unsigned char pixels[296*194];
-       strlcpy(cache->name, skinname, sizeof(cache->name));
+       dp_strlcpy(cache->name, skinname, sizeof(cache->name));
        dpsnprintf(name, sizeof(name), "skins/%s.pcx", cache->name);
        if (developer_loading.integer)
                Con_Printf("loading %s\n", name);
@@ -8568,14 +8610,14 @@ static int RSurf_FindWaterPlaneForSurface(const msurface_t *surface)
        // render multiple smaller batches
 }
 
-void RSurf_SetupDepthAndCulling(void)
+void RSurf_SetupDepthAndCulling(bool ui)
 {
        // submodels are biased to avoid z-fighting with world surfaces that they
        // may be exactly overlapping (avoids z-fighting artifacts on certain
        // doors and things in Quake maps)
        GL_DepthRange(0, (rsurface.texture->currentmaterialflags & MATERIALFLAG_SHORTDEPTHRANGE) ? 0.0625 : 1);
        GL_PolygonOffset(rsurface.basepolygonfactor + rsurface.texture->biaspolygonfactor, rsurface.basepolygonoffset + rsurface.texture->biaspolygonoffset);
-       GL_DepthTest(!(rsurface.texture->currentmaterialflags & MATERIALFLAG_NODEPTHTEST));
+       GL_DepthTest(!ui && !(rsurface.texture->currentmaterialflags & MATERIALFLAG_NODEPTHTEST));
        GL_CullFace((rsurface.texture->currentmaterialflags & MATERIALFLAG_NOCULLFACE) ? GL_NONE : r_refdef.view.cullface_back);
 }
 
@@ -8590,7 +8632,7 @@ static void R_DrawTextureSurfaceList_Sky(int texturenumsurfaces, const msurface_
                return;
        R_SetupShader_Generic_NoTexture(false, false);
        skyrenderlater = true;
-       RSurf_SetupDepthAndCulling();
+       RSurf_SetupDepthAndCulling(false);
        GL_DepthMask(true);
 
        // add the vertices of the surfaces to a world bounding box so we can scissor the sky render later
@@ -8746,6 +8788,8 @@ static void R_DrawTextureSurfaceList_ShowSurfaces(int texturenumsurfaces, const
        int k;
        const msurface_t *surface;
        float surfacecolor4f[4];
+       float c[4];
+       texture_t *t = rsurface.texture;
 
 //     R_Mesh_ResetTextureState();
        R_SetupShader_Generic_NoTexture(false, false);
@@ -8753,18 +8797,70 @@ static void R_DrawTextureSurfaceList_ShowSurfaces(int texturenumsurfaces, const
        GL_BlendFunc(GL_ONE, GL_ZERO);
        GL_DepthMask(writedepth);
 
-       RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_VERTEXCOLOR | BATCHNEED_ARRAY_TEXCOORD | BATCHNEED_ALWAYSCOPY, texturenumsurfaces, texturesurfacelist);
-       vi = 0;
-       for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+       switch (r_showsurfaces.integer)
        {
-               surface = texturesurfacelist[texturesurfaceindex];
-               k = (int)(((size_t)surface) / sizeof(msurface_t));
-               Vector4Set(surfacecolor4f, (k & 0xF) * (1.0f / 16.0f), (k & 0xF0) * (1.0f / 256.0f), (k & 0xF00) * (1.0f / 4096.0f), 1);
-               for (j = 0;j < surface->num_vertices;j++)
-               {
-                       Vector4Copy(surfacecolor4f, rsurface.batchlightmapcolor4f + 4 * vi);
-                       vi++;
-               }
+               case 1:
+               default:
+                       RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_VERTEXCOLOR | BATCHNEED_ALWAYSCOPY, texturenumsurfaces, texturesurfacelist);
+                       vi = 0;
+                       for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+                       {
+                               surface = texturesurfacelist[texturesurfaceindex];
+                               k = (int)(((size_t)surface) / sizeof(msurface_t));
+                               Vector4Set(surfacecolor4f, (k & 0xF) * (1.0f / 16.0f), (k & 0xF0) * (1.0f / 256.0f), (k & 0xF00) * (1.0f / 4096.0f), 1);
+                               for (j = 0;j < surface->num_vertices;j++)
+                               {
+                                       Vector4Copy(surfacecolor4f, rsurface.batchlightmapcolor4f + 4 * vi);
+                                       vi++;
+                               }
+                       }
+                       break;
+               case 3:
+                       if(t && t->currentskinframe)
+                       {
+                               Vector4Copy(t->currentskinframe->avgcolor, c);
+                               c[3] *= t->currentalpha;
+                       }
+                       else
+                       {
+                               Vector4Set(c, 1, 0, 1, 1);
+                       }
+                       if (t && (t->pantstexture || t->shirttexture))
+                       {
+                               VectorMAM(0.7, t->render_colormap_pants, 0.3, t->render_colormap_shirt, c);
+                       }
+                       VectorScale(c, 2 * r_refdef.view.colorscale, c);
+                       if(t->currentmaterialflags & MATERIALFLAG_WATERALPHA)
+                               c[3] *= r_wateralpha.value;
+                       RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_VERTEXCOLOR | BATCHNEED_ALWAYSCOPY, texturenumsurfaces, texturesurfacelist);
+                       vi = 0;
+                       if (rsurface.modellightmapcolor4f)
+                       {
+                               for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+                               {
+                                       surface = texturesurfacelist[texturesurfaceindex];
+                                       for (j = 0;j < surface->num_vertices;j++)
+                                       {
+                                               float *ptr = rsurface.batchlightmapcolor4f + 4 * vi;
+                                               Vector4Multiply(ptr, c, ptr);
+                                               vi++;
+                                       }
+                               }
+                       }
+                       else
+                       {
+                               for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+                               {
+                                       surface = texturesurfacelist[texturesurfaceindex];
+                                       for (j = 0;j < surface->num_vertices;j++)
+                                       {
+                                               float *ptr = rsurface.batchlightmapcolor4f + 4 * vi;
+                                               Vector4Copy(c, ptr);
+                                               vi++;
+                                       }
+                               }
+                       }
+                       break;
        }
        R_Mesh_PrepareVertices_Generic_Arrays(rsurface.batchnumvertices, rsurface.batchvertex3f, rsurface.batchlightmapcolor4f, rsurface.batchtexcoordtexture2f);
        RSurf_DrawBatch();
@@ -8773,7 +8869,7 @@ static void R_DrawTextureSurfaceList_ShowSurfaces(int texturenumsurfaces, const
 static void R_DrawModelTextureSurfaceList(int texturenumsurfaces, const msurface_t **texturesurfacelist, qbool writedepth, qbool prepass, qbool ui)
 {
        CHECKGLERROR
-       RSurf_SetupDepthAndCulling();
+       RSurf_SetupDepthAndCulling(ui);
        if (r_showsurfaces.integer && r_refdef.view.showdebug)
        {
                R_DrawTextureSurfaceList_ShowSurfaces(texturenumsurfaces, texturesurfacelist, writedepth);
@@ -8835,7 +8931,7 @@ static void R_DrawSurface_TransparentCallback(const entity_render_t *ent, const
                                GL_DepthMask(true);
 //                             R_Mesh_ResetTextureState();
                        }
-                       RSurf_SetupDepthAndCulling();
+                       RSurf_SetupDepthAndCulling(false);
                        RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_ALLOWMULTIDRAW, texturenumsurfaces, texturesurfacelist);
                        R_SetupShader_DepthOrShadow(false, false, !!rsurface.batchskeletaltransform3x4);
                        R_Mesh_PrepareVertices_Vertex3f(rsurface.batchnumvertices, rsurface.batchvertex3f, rsurface.batchvertex3f_vertexbuffer, rsurface.batchvertex3f_bufferoffset);
@@ -8909,7 +9005,7 @@ static void R_DrawTextureSurfaceList_DepthOnly(int texturenumsurfaces, const msu
                return;
        if (r_fb.water.renderingscene && (rsurface.texture->currentmaterialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFLECTION)))
                return;
-       RSurf_SetupDepthAndCulling();
+       RSurf_SetupDepthAndCulling(false);
        RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_ALLOWMULTIDRAW, texturenumsurfaces, texturesurfacelist);
        R_Mesh_PrepareVertices_Vertex3f(rsurface.batchnumvertices, rsurface.batchvertex3f, rsurface.batchvertex3f_vertexbuffer, rsurface.batchvertex3f_bufferoffset);
        R_SetupShader_DepthOrShadow(false, false, !!rsurface.batchskeletaltransform3x4);
@@ -10042,7 +10138,7 @@ void R_DrawModelSurfaces(entity_render_t *ent, qbool skysurfaces, qbool writedep
                // Now check if update flags are set on any surfaces that are visible
                if (r_q1bsp_lightmap_updates_hidden_surfaces.integer)
                {
-                       /* 
+                       /*
                         * We can do less frequent texture uploads (approximately 10hz for animated
                         * lightstyles) by rebuilding lightmaps on surfaces that are not currently visible.
                         * For optimal efficiency, this includes the submodels of the worldmodel, so we
index 723e9fb4ce3ee2028ffcc20b6924f1e02955dedd..a68daf5b68e539e032441a0d715e48d3977e0a5d 100644 (file)
@@ -1295,7 +1295,10 @@ void R_Mod_CompileShadowMap(entity_render_t *ent, vec3_t relativelightorigin, ve
        // exceeding the number of triangles in a single mesh) we have to make sure
        // that we make only a single mesh - so over-estimate the size of the mesh
        // to match the model.
-       r_shadow_compilingrtlight->static_meshchain_shadow_shadowmap = Mod_ShadowMesh_Begin(r_main_mempool, model->surfmesh.num_vertices, model->surfmesh.num_triangles);
+       // bones_was_here: +128 works around BUG https://github.com/DarkPlacesEngine/darkplaces/issues/119
+       // +64 was enough to start the map without triggering ASan, +96 was enough to also edit the light.
+       // See also: warning in Mod_ShadowMesh_AddMesh
+       r_shadow_compilingrtlight->static_meshchain_shadow_shadowmap = Mod_ShadowMesh_Begin(r_main_mempool, model->surfmesh.num_vertices, model->surfmesh.num_triangles + 128);
        R_Shadow_PrepareShadowSides(model->surfmesh.num_triangles);
        for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
        {
index 1e3e1d7e5d79a63afd47e0840e0f05af35a14bdd..049240b63bccb4d80c842c4c3f577c7345d19620 100644 (file)
@@ -1291,7 +1291,7 @@ static rtexture_t *R_SetupTexture(rtexturepool_t *rtexturepool, const char *iden
 
        glt = (gltexture_t *)Mem_ExpandableArray_AllocRecord(&texturearray);
        if (identifier)
-               strlcpy (glt->identifier, identifier, sizeof(glt->identifier));
+               dp_strlcpy (glt->identifier, identifier, sizeof(glt->identifier));
        glt->pool = pool;
        glt->chain = pool->gltchain;
        pool->gltchain = glt;
@@ -1382,7 +1382,7 @@ rtexture_t *R_LoadTextureRenderBuffer(rtexturepool_t *rtexturepool, const char *
 
        glt = (gltexture_t *)Mem_ExpandableArray_AllocRecord(&texturearray);
        if (identifier)
-               strlcpy (glt->identifier, identifier, sizeof(glt->identifier));
+               dp_strlcpy (glt->identifier, identifier, sizeof(glt->identifier));
        glt->pool = pool;
        glt->chain = pool->gltchain;
        pool->gltchain = glt;
@@ -1686,7 +1686,7 @@ rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filen
 
                        texinfo = R_GetTexTypeInfo(textype, flags);
 
-                       strlcpy (glt->identifier, vabuf, sizeof(glt->identifier));
+                       dp_strlcpy(glt->identifier, vabuf, sizeof(glt->identifier));
                        glt->pool = pool;
                        glt->chain = pool->gltchain;
                        pool->gltchain = glt;
@@ -1969,7 +1969,8 @@ rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filen
                unsigned char *p = mipnewpixels;
                for (i = bytesperblock == 16 ? 8 : 0;i < (int)mipsize_total;i += bytesperblock, p += 4)
                {
-                       c = mippixels_start[i] + 256*mippixels_start[i+1] + 65536*mippixels_start[i+2] + 16777216*mippixels_start[i+3];
+                       // UBSan: unsigned literals because promotion to int causes signed overflow when mippixels_start >= 128
+                       c = mippixels_start[i] + 256u*mippixels_start[i+1] + 65536u*mippixels_start[i+2] + 16777216u*mippixels_start[i+3];
                        p[2] = (((c >> 11) & 0x1F) + ((c >> 27) & 0x1F)) * (0.5f / 31.0f * 255.0f);
                        p[1] = (((c >>  5) & 0x3F) + ((c >> 21) & 0x3F)) * (0.5f / 63.0f * 255.0f);
                        p[0] = (((c      ) & 0x1F) + ((c >> 16) & 0x1F)) * (0.5f / 31.0f * 255.0f);
@@ -2014,7 +2015,8 @@ rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filen
                {
                        for (i = bytesperblock == 16 ? 8 : 0;i < mipsize;i += bytesperblock)
                        {
-                               c = mippixels[i] + 256*mippixels[i+1] + 65536*mippixels[i+2] + 16777216*mippixels[i+3];
+                               // UBSan: unsigned literals because promotion to int causes signed overflow when mippixels >= 128
+                               c = mippixels[i] + 256u*mippixels[i+1] + 65536u*mippixels[i+2] + 16777216u*mippixels[i+3];
                                avgcolor[0] += ((c >> 11) & 0x1F) + ((c >> 27) & 0x1F);
                                avgcolor[1] += ((c >>  5) & 0x3F) + ((c >> 21) & 0x3F);
                                avgcolor[2] += ((c      ) & 0x1F) + ((c >> 16) & 0x1F);
@@ -2164,7 +2166,7 @@ rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filen
        texinfo = R_GetTexTypeInfo(textype, flags);
 
        glt = (gltexture_t *)Mem_ExpandableArray_AllocRecord(&texturearray);
-       strlcpy (glt->identifier, filename, sizeof(glt->identifier));
+       dp_strlcpy (glt->identifier, filename, sizeof(glt->identifier));
        glt->pool = pool;
        glt->chain = pool->gltchain;
        pool->gltchain = glt;
index 2c8b1c306a4ade4ade301a4307d4073141e2a9a9..46efbf0721fe6ac23334c0a201fe1f9502d918b4 100644 (file)
--- a/glquake.h
+++ b/glquake.h
 
 //====================================================
 
-//#define DEBUGGL
-
-#ifdef DEBUGGL
-#ifdef USE_GLES2
-#define CHECKGLERROR {if (gl_paranoid.integer){if (gl_printcheckerror.integer) Con_Printf("CHECKGLERROR at %s:%d\n", __FILE__, __LINE__);gl_errornumber = glGetError();if (gl_errornumber) GL_PrintError(gl_errornumber, __FILE__, __LINE__);}}
-#else
-#define CHECKGLERROR {if (gl_paranoid.integer){if (gl_printcheckerror.integer) Con_Printf("CHECKGLERROR at %s:%d\n", __FILE__, __LINE__);gl_errornumber = qglGetError ? qglGetError() : 0;if (gl_errornumber) GL_PrintError(gl_errornumber, __FILE__, __LINE__);}}
-#endif //USE_GLES2
-extern int gl_errornumber;
-void GL_PrintError(int errornumber, const char *filename, int linenumber);
-#else
-#define CHECKGLERROR
-#endif //DEBUGGL
-
 #ifndef USE_GLES2
 // this is defined if the SDL_opengl.h is included
 #ifndef GL_ZERO
@@ -1015,7 +1001,6 @@ extern void (GLAPIENTRY *qglViewport)(GLint x, GLint y, GLsizei width, GLsizei h
 #define qglViewport glViewport
 #endif //USE_GLES2
 
-#endif //DEBUGGL
 #define GL_COLOR_ATTACHMENT0                0x8CE0
 #define GL_COLOR_ATTACHMENT1                0x8CE1
 #define GL_COLOR_ATTACHMENT2                0x8CE2
@@ -1049,4 +1034,29 @@ extern void (GLAPIENTRY *qglViewport)(GLint x, GLint y, GLsizei width, GLsizei h
 #define GL_TEXTURE_3D                          0x806F
 
 #define GL_HALF_FLOAT                                    0x140B
+#define GL_MAJOR_VERSION                  0x821B
+#define GL_MINOR_VERSION                  0x821C
 #define GL_NUM_EXTENSIONS                 0x821D
+
+
+//====================================================
+
+//#define DEBUGGL
+
+#ifdef DEBUGGL
+void GL_PrintError(GLenum errornumber, const char *filename, unsigned int linenumber);
+#define CHECKGLERROR { \
+       if (gl_paranoid.integer) { \
+               GLenum gl_errornumber; \
+               if (gl_printcheckerror.integer) \
+                       Con_Printf("CHECKGLERROR at %s:%d\n", __FILE__, __LINE__); \
+               if (qglGetError) /* bones_was_here: is this pointer check still necessary? */ \
+                       while ((gl_errornumber = qglGetError())) \
+                               GL_PrintError(gl_errornumber, __FILE__, __LINE__); \
+       }}
+#else
+#define CHECKGLERROR
+#endif //DEBUGGL
+
+
+#endif //GLQUAKE_H
diff --git a/host.c b/host.c
index aa8fb4c32bf9d712113f2bce98d71ec6d06f77e9..3f06d38e8ead78a23507cd5424575a62bc7e399c 100644 (file)
--- a/host.c
+++ b/host.c
@@ -42,10 +42,8 @@ host_static_t host;
 
 // pretend frames take this amount of time (in seconds), 0 = realtime
 cvar_t host_framerate = {CF_CLIENT | CF_SERVER, "host_framerate","0", "locks frame timing to this value in seconds, 0.05 is 20fps for example, note that this can easily run too fast, use cl_maxfps if you want to limit your framerate instead, or sys_ticrate to limit server speed"};
-cvar_t cl_maxphysicsframesperserverframe = {CF_CLIENT, "cl_maxphysicsframesperserverframe","10", "maximum number of physics frames per server frame"};
 // shows time used by certain subsystems
 cvar_t host_speeds = {CF_CLIENT | CF_SERVER, "host_speeds","0", "reports how much time is used in server/graphics/sound"};
-cvar_t host_maxwait = {CF_CLIENT | CF_SERVER, "host_maxwait","1000", "maximum sleep time requested from the operating system in millisecond. Larger sleeps will be done using multiple host_maxwait length sleeps. Lowering this value will increase CPU load, but may help working around problems with accuracy of sleep times."};
 
 cvar_t developer = {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "developer","0", "shows debugging messages and information (recommended for all developers and level designers); the value -1 also suppresses buffering and logging these messages"};
 cvar_t developer_extra = {CF_CLIENT | CF_SERVER, "developer_extra", "0", "prints additional debugging messages, often very verbose!"};
@@ -90,6 +88,10 @@ void Host_Error (const char *error, ...)
        static char hosterrorstring2[MAX_INPUTLINE]; // THREAD UNSAFE
        static qbool hosterror = false;
        va_list argptr;
+       int outfd = sys.outfd;
+
+       // set output to stderr
+       sys.outfd = fileno(stderr);
 
        // turn off rcon redirect if it was active when the crash occurred
        // to prevent loops when it is a networking problem
@@ -104,41 +106,37 @@ void Host_Error (const char *error, ...)
        // LadyHavoc: if crashing very early, or currently shutting down, do
        // Sys_Error instead
        if (host.framecount < 3 || host.state == host_shutdown)
-               Sys_Error ("Host_Error: %s", hosterrorstring1);
+               Sys_Error ("Host_Error during %s: %s", host.framecount < 3 ? "startup" : "shutdown", hosterrorstring1);
 
        if (hosterror)
                Sys_Error ("Host_Error: recursively entered (original error was: %s    new error is: %s)", hosterrorstring2, hosterrorstring1);
        hosterror = true;
 
-       strlcpy(hosterrorstring2, hosterrorstring1, sizeof(hosterrorstring2));
+       dp_strlcpy(hosterrorstring2, hosterrorstring1, sizeof(hosterrorstring2));
 
        CL_Parse_DumpPacket();
-
        CL_Parse_ErrorCleanUp();
 
-       //PR_Crash();
-
        // print out where the crash happened, if it was caused by QC (and do a cleanup)
-       PRVM_Crash(SVVM_prog);
-       PRVM_Crash(CLVM_prog);
-#ifdef CONFIG_MENU
-       PRVM_Crash(MVM_prog);
-#endif
-
-       Cvar_SetValueQuick(&csqc_progcrc, -1);
-       Cvar_SetValueQuick(&csqc_progsize, -1);
+       PRVM_Crash();
 
        if(host.hook.SV_Shutdown)
                host.hook.SV_Shutdown();
 
        if (cls.state == ca_dedicated)
-               Sys_Error ("Host_Error: %s",hosterrorstring2);  // dedicated servers exit
+               Sys_Error("Host_Error: %s", hosterrorstring1);        // dedicated servers exit
+
+       // prevent an endless loop if the error was triggered by a command
+       Cbuf_Clear(cmd_local->cbuf);
 
-       CL_Disconnect();
-       cls.demonum = -1;
+       CL_DisconnectEx(false, "Host_Error: %s", hosterrorstring1);
+       cls.demonum = -1; // stop demo loop
 
        hosterror = false;
 
+       // restore configured outfd
+       sys.outfd = outfd;
+
        Host_AbortCurrentFrame();
 }
 
@@ -157,7 +155,7 @@ static void Host_Quit_f(cmd_state_t *cmd)
 
 static void Host_Version_f(cmd_state_t *cmd)
 {
-       Con_Printf("Version: %s build %s\n", gamename, buildstring);
+       Con_Printf("Version: %s\n", engineversion);
 }
 
 static void Host_Framerate_c(cvar_t *var)
@@ -200,9 +198,11 @@ void Host_SaveConfig(const char *file)
                f = FS_OpenRealFile(file, "wb", false);
                if (!f)
                {
-                       Con_Printf(CON_ERROR "Couldn't write %s.\n", file);
+                       Con_Printf(CON_ERROR "Couldn't write %s\n", file);
                        return;
                }
+               else
+                       Con_Printf("Saving config to %s ...\n", file);
 
                Key_WriteBindings (f);
                Cvar_WriteVariables (&cvars_all, f);
@@ -215,10 +215,8 @@ static void Host_SaveConfig_f(cmd_state_t *cmd)
 {
        const char *file = CONFIGFILENAME;
 
-       if(Cmd_Argc(cmd) >= 2) {
+       if(Cmd_Argc(cmd) >= 2)
                file = Cmd_Argv(cmd, 1);
-               Con_Printf("Saving to %s\n", file);
-       }
 
        Host_SaveConfig(file);
 }
@@ -235,7 +233,10 @@ static void Host_AddConfigText(cmd_state_t *cmd)
                Cbuf_InsertText(cmd, "alias startmap_sp \"map start\"\nalias startmap_dm \"map start\"\nexec teu.rc\n");
        else
                Cbuf_InsertText(cmd, "alias startmap_sp \"map start\"\nalias startmap_dm \"map start\"\nexec " STARTCONFIGFILENAME "\n");
-       Cbuf_Execute(cmd->cbuf);
+
+       // if quake.rc is missing, use default
+       if (!FS_FileExists(STARTCONFIGFILENAME))
+               Cbuf_InsertText(cmd, "exec default.cfg\nexec " CONFIGFILENAME "\nexec autoexec.cfg\n");
 }
 
 /*
@@ -247,14 +248,17 @@ Resets key bindings and cvars to defaults and then reloads scripts
 */
 static void Host_LoadConfig_f(cmd_state_t *cmd)
 {
-       // reset all cvars, commands and aliases to init values
-       Cmd_RestoreInitState();
 #ifdef CONFIG_MENU
-       // prepend a menu restart command to execute after the config
-       Cbuf_InsertText(cmd_local, "\nmenu_restart\n");
+       // Xonotic QC complains/breaks if its cvars are deleted before its m_shutdown() is called
+       if(MR_Shutdown)
+               MR_Shutdown();
+       // append a menu restart command to execute after the config
+       Cbuf_AddText(cmd, "\nmenu_restart\n");
 #endif
+       // reset all cvars, commands and aliases to init values
+       Cmd_RestoreInitState();
        // reset cvars to their defaults, and then exec startup scripts again
-       Host_AddConfigText(cmd_local);
+       Host_AddConfigText(cmd);
 }
 
 /*
@@ -270,11 +274,9 @@ static void Host_InitLocal (void)
        Cmd_AddCommand(CF_SHARED, "saveconfig", Host_SaveConfig_f, "save settings to config.cfg (or a specified filename) immediately (also automatic when quitting)");
        Cmd_AddCommand(CF_SHARED, "loadconfig", Host_LoadConfig_f, "reset everything and reload configs");
        Cmd_AddCommand(CF_SHARED, "sendcvar", SendCvar_f, "sends the value of a cvar to the server as a sentcvar command, for use by QuakeC");
-       Cvar_RegisterVariable (&cl_maxphysicsframesperserverframe);
        Cvar_RegisterVariable (&host_framerate);
        Cvar_RegisterCallback (&host_framerate, Host_Framerate_c);
        Cvar_RegisterVariable (&host_speeds);
-       Cvar_RegisterVariable (&host_maxwait);
        Cvar_RegisterVariable (&host_isclient);
 
        Cvar_RegisterVariable (&developer);
@@ -292,7 +294,6 @@ static void Host_InitLocal (void)
 
 char engineversion[128];
 
-qbool sys_nostdout = false;
 
 static qfile_t *locksession_fh = NULL;
 static qbool locksession_run = false;
@@ -367,9 +368,12 @@ Host_Init
 static void Host_Init (void)
 {
        int i;
-       const char* os;
        char vabuf[1024];
 
+       Sys_SDL_Init();
+
+       Memory_Init();
+
        host.hook.ConnectLocal = NULL;
        host.hook.Disconnect = NULL;
        host.hook.ToggleMenu = NULL;
@@ -420,9 +424,9 @@ static void Host_Init (void)
                gl_printcheckerror.integer = 1;gl_printcheckerror.string = "1";
        }
 
-// COMMANDLINEOPTION: Console: -nostdout disables text output to the terminal the game was launched from
-       if (Sys_CheckParm("-nostdout"))
-               sys_nostdout = 1;
+       // -dedicated is checked in SV_ServerOptions() but that's too late for Cvar_RegisterVariable() to skip all the client-only cvars
+       if (Sys_CheckParm ("-dedicated") || !cl_available)
+               cls.state = ca_dedicated;
 
        // initialize console command/cvar/alias/command execution systems
        Cmd_Init();
@@ -431,6 +435,7 @@ static void Host_Init (void)
        Memory_Init_Commands();
 
        // initialize console and logging and its cvars/commands
+       // this enables Con_Printf() messages to be coloured
        Con_Init();
 
        // initialize various cvars that could not be initialized earlier
@@ -439,12 +444,11 @@ static void Host_Init (void)
        Sys_Init_Commands();
        COM_Init_Commands();
 
-       // initialize filesystem (including fs_basedir, fs_gamedir, -game, scr_screenshot_name)
+       // initialize filesystem (including fs_basedir, fs_gamedir, -game, scr_screenshot_name, gamename)
        FS_Init();
 
-       // construct a version string for the corner of the console
-       os = DP_OS_NAME;
-       dpsnprintf (engineversion, sizeof (engineversion), "%s %s %s", gamename, os, buildstring);
+       // ASAP! construct a version string for the corner of the console and for crash messages
+       dpsnprintf (engineversion, sizeof (engineversion), "%s %s%s, buildstring: %s", gamename, DP_OS_NAME, cls.state == ca_dedicated ? " dedicated" : "", buildstring);
        Con_Printf("%s\n", engineversion);
 
        // initialize process nice level
@@ -483,13 +487,7 @@ static void Host_Init (void)
        // here comes the not so critical stuff
 
        Host_AddConfigText(cmd_local);
-
-       // if quake.rc is missing, use default
-       if (!FS_FileExists("quake.rc"))
-       {
-               Cbuf_AddText(cmd_local, "exec default.cfg\nexec " CONFIGFILENAME "\nexec autoexec.cfg\n");
-               Cbuf_Execute(cmd_local->cbuf);
-       }
+       Cbuf_Execute(cmd_local->cbuf); // cannot be in Host_AddConfigText as that would cause Host_LoadConfig_f to loop!
 
        host.state = host_active;
 
@@ -497,12 +495,16 @@ static void Host_Init (void)
 
        Log_Start();
 
-       // put up the loading image so the user doesn't stare at a black screen...
-       SCR_BeginLoadingPlaque(true);
-#ifdef CONFIG_MENU
        if (cls.state != ca_dedicated)
+       {
+               // put up the loading image so the user doesn't stare at a black screen...
+               SCR_BeginLoadingPlaque(true);
+               S_Startup();
+#ifdef CONFIG_MENU
                MR_Init();
 #endif
+       }
+
        // check for special benchmark mode
 // COMMANDLINEOPTION: Client: -benchmark <demoname> runs a timedemo and quits, results of any timedemo can be found in gamedir/benchmark.log (for example id1/benchmark.log)
        i = Sys_CheckParm("-benchmark");
@@ -559,25 +561,13 @@ static void Host_Init (void)
 ===============
 Host_Shutdown
 
-FIXME: this is a callback from Sys_Quit and Sys_Error.  It would be better
-to run quit through here before the final handoff to the sys code.
+Cleanly shuts down after the main loop exits.
 ===============
 */
-void Host_Shutdown(void)
+static void Host_Shutdown(void)
 {
-       static qbool isdown = false;
-
-       if (isdown)
-       {
-               Con_Print("recursive shutdown\n");
-               return;
-       }
-       if (setjmp(host.abortframe))
-       {
-               Con_Print("aborted the quitting frame?!?\n");
-               return;
-       }
-       isdown = true;
+       if (Sys_CheckParm("-profilegameonly"))
+               Sys_AllowProfiling(false);
 
        if(cls.state != ca_dedicated)
                CL_Shutdown();
@@ -602,7 +592,7 @@ void Host_Shutdown(void)
        TaskQueue_Shutdown();
        Thread_Shutdown();
        Cmd_Shutdown();
-       Sys_Shutdown();
+       Sys_SDL_Shutdown();
        Log_Close();
        Crypto_Shutdown();
 
@@ -638,56 +628,26 @@ static double Host_Frame(double time)
        // Run any downloads
        Curl_Frame();
 
+       // get new SDL events and add commands from keybindings to the cbuf
+       Sys_SDL_HandleEvents();
+
        // process console commands
        Cbuf_Frame(host.cbuf);
 
        R_TimeReport("---");
 
-       sv_wait = SV_Frame(time);
-       cl_wait = CL_Frame(time);
-
-//     Con_Printf("%6.0f %6.0f\n", cl_wait * 1000000.0, sv_wait * 1000000.0);
+       // if the accumulators haven't become positive yet, wait a while
+       sv_wait = - SV_Frame(time);
+       cl_wait = - CL_Frame(time);
 
        Mem_CheckSentinelsGlobal();
 
-       if(host.restless)
-               return 0;
-
-       // if the accumulators haven't become positive yet, wait a while
        if (cls.state == ca_dedicated)
-               return sv_wait * -1000000.0; // dedicated
+               return sv_wait; // dedicated
        else if (!sv.active || svs.threaded)
-               return cl_wait * -1000000.0; // connected to server, main menu, or server is on different thread
+               return cl_wait; // connected to server, main menu, or server is on different thread
        else
-               return max(cl_wait, sv_wait) * -1000000.0; // listen server or singleplayer
-}
-
-static inline void Host_Sleep(double time)
-{
-       double delta, time0;
-
-       if(host_maxwait.value <= 0)
-               time = min(time, 1000000.0);
-       else
-               time = min(time, host_maxwait.value * 1000.0);
-       if(time < 1)
-               time = 1; // because we cast to int
-
-       time0 = Sys_DirtyTime();
-       if (sv_checkforpacketsduringsleep.integer && !sys_usenoclockbutbenchmark.integer && !svs.threaded) {
-               NetConn_SleepMicroseconds((int)time);
-               if (cls.state != ca_dedicated)
-                       NetConn_ClientFrame(); // helps server browser get good ping values
-               // TODO can we do the same for ServerFrame? Probably not.
-       }
-       else
-               Sys_Sleep((int)time);
-       delta = Sys_DirtyTime() - time0;
-       if (delta < 0 || delta >= 1800)
-               delta = 0;
-       host.sleeptime += delta;
-//                     R_TimeReport("sleep");
-       return;
+               return min(cl_wait, sv_wait); // listen server or singleplayer
 }
 
 // Cloudwalk: Most overpowered function declaration...
@@ -713,7 +673,7 @@ static inline double Host_UpdateTime (double newtime, double oldtime)
 
 void Host_Main(void)
 {
-       double time, newtime, oldtime, sleeptime;
+       double time, oldtime, sleeptime;
 
        Host_Init(); // Start!
 
@@ -721,7 +681,7 @@ void Host_Main(void)
        oldtime = Sys_DirtyTime();
 
        // Main event loop
-       while(host.state != host_shutdown)
+       while(host.state < host_shutdown) // see Sys_HandleCrash() comments
        {
                // Something bad happened, or the server disconnected
                if (setjmp(host.abortframe))
@@ -730,20 +690,15 @@ void Host_Main(void)
                        continue;
                }
 
-               newtime = host.dirtytime = Sys_DirtyTime();
-               host.realtime += time = Host_UpdateTime(newtime, oldtime);
+               host.dirtytime = Sys_DirtyTime();
+               host.realtime += time = Host_UpdateTime(host.dirtytime, oldtime);
+               oldtime = host.dirtytime;
 
                sleeptime = Host_Frame(time);
-               oldtime = newtime;
-
-               if (sleeptime >= 1)
-               {
-                       Host_Sleep(sleeptime);
-                       continue;
-               }
-
-               host.framecount++;
+               ++host.framecount;
+               sleeptime -= Sys_DirtyTime() - host.dirtytime; // execution time
+               host.sleeptime = Sys_Sleep(sleeptime);
        }
 
-       return;
+       Host_Shutdown();
 }
diff --git a/host.h b/host.h
index 26465423839b5523830ae599f25370180699f1c6..779ccc842b370af6082205885ac2e6619334632b 100644 (file)
--- a/host.h
+++ b/host.h
@@ -5,25 +5,38 @@
 #include "qtypes.h"
 #include "qdefs.h"
 #include "cmd.h"
+#include "cvar.h"
+
+extern cvar_t developer;
+extern cvar_t developer_entityparsing;
+extern cvar_t developer_extra;
+extern cvar_t developer_insane;
+extern cvar_t developer_loadfile;
+extern cvar_t developer_loading;
+extern cvar_t host_isclient;
+extern cvar_t sessionid;
 
 struct cmd_state_s;
 
 typedef enum host_state_e
 {
-       host_shutdown,
        host_init,
        host_loading,
-       host_active
+       host_active,
+       // states >= host_shutdown cause graceful shutdown, see Sys_HandleCrash() comments
+       host_shutdown,
+       host_failing, ///< crashing
+       host_failed ///< crashed or aborted, SDL dialog open
 } host_state_t;
 
 typedef struct host_static_s
 {
        jmp_buf abortframe;
        int state;
-       int framecount; // incremented every frame, never reset (checked by Host_Error and Host_SaveConfig_f)
+       unsigned int framecount; // incremented every frame, never reset (checked by Host_Error and Host_SaveConfig_f)
        double realtime; // the accumulated mainloop time since application started (with filtering), without any slowmo or clamping
        double dirtytime; // the main loop wall time for this frame, equal to Sys_DirtyTime() at the start of this host frame
-       double sleeptime; // time spent sleeping overall
+       double sleeptime; // time spent sleeping after the last frame
        qbool restless; // don't sleep
        qbool paused; // global paused state, pauses both client and server
        cmd_buf_t *cbuf;
@@ -43,7 +56,6 @@ typedef struct host_static_s
 extern host_static_t host;
 
 void Host_Main(void);
-void Host_Shutdown(void);
 void Host_Error(const char *error, ...) DP_FUNC_PRINTF(1) DP_FUNC_NORETURN;
 void Host_LockSession(void);
 void Host_UnlockSession(void);
diff --git a/image.c b/image.c
index ed498ed6562ce832080410a4fc25a9378aeb4727..15835797ecde5bf6c2df3ed18f4402a55a724a5d 100644 (file)
--- a/image.c
+++ b/image.c
@@ -921,7 +921,7 @@ void Image_StripImageExtension (const char *in, char *out, size_t size_out)
        if (ext && (!strcmp(ext, "tga") || !strcmp(ext, "pcx") || !strcmp(ext, "lmp") || !strcmp(ext, "png") || !strcmp(ext, "jpg") || !strcmp(ext, "wal")))
                FS_StripExtension(in, out, size_out);
        else
-               strlcpy(out, in, size_out);
+               dp_strlcpy(out, in, size_out);
 }
 
 static unsigned char image_linearfromsrgb[256];
@@ -1059,14 +1059,14 @@ unsigned char *loadimagepixelsbgra (const char *filename, qbool complain, qbool
                        *c = '#';
        path[0] = 0;
        name[0] = 0;
-       strlcpy(afterpath, basename, sizeof(afterpath));
+       dp_strlcpy(afterpath, basename, sizeof(afterpath));
        if (strchr(basename, '/'))
        {
                int i;
                for (i = 0;i < (int)sizeof(path)-1 && basename[i] != '/' && basename[i];i++)
                        path[i] = basename[i];
                path[i] = 0;
-               strlcpy(afterpath, basename + i + 1, sizeof(afterpath));
+               dp_strlcpy(afterpath, basename + i + 1, sizeof(afterpath));
        }
        if (gamemode == GAME_TENEBRAE)
                firstformat = imageformats_tenebrae;
diff --git a/jpeg.c b/jpeg.c
index beab84335221c9ad4e3f06cb4e765fdc8bbc45b8..70eb3e472d1478c5ff7736447e84e488ef99e767 100644 (file)
--- a/jpeg.c
+++ b/jpeg.c
@@ -1019,7 +1019,7 @@ static void CompressedImageCache_Add(const char *imagename, size_t maxsize, void
                return; // can't add this
 
        i = (CompressedImageCacheItem*) Z_Malloc(sizeof(CompressedImageCacheItem));
-       strlcpy(i->imagename, imagename, sizeof(i->imagename));
+       dp_strlcpy(i->imagename, imagename, sizeof(i->imagename));
        i->maxsize = maxsize;
        i->compressed = compressed;
        i->compressed_size = compressed_size;
diff --git a/keys.c b/keys.c
index cef9dc91d2e21286aed5e1d02702230b34fb9e74..a3a6d9268601045bd102472293893308021bc966 100644 (file)
--- a/keys.c
+++ b/keys.c
@@ -92,18 +92,20 @@ static void Key_History_Init(void)
 
 static void Key_History_Shutdown(void)
 {
-       // TODO write history to a file
-
 // not necessary for mobile
 #ifndef DP_MOBILETOUCH
        qfile_t *historyfile = FS_OpenRealFile("darkplaces_history.txt", "w", false);
        if(historyfile)
        {
                int i;
+
+               Con_Print("Saving command history to darkplaces_history.txt ...\n");
                for(i = 0; i < CONBUFFER_LINES_COUNT(&history); ++i)
                        FS_Printf(historyfile, "%s\n", ConBuffer_GetLine(&history, i));
                FS_Close(historyfile);
        }
+       else
+               Con_Print(CON_ERROR "Couldn't write darkplaces_history.txt\n");
 #endif
 
        ConBuffer_Shutdown(&history);
@@ -127,7 +129,7 @@ static qbool Key_History_Get_foundCommand(void)
 {
        if (!history_matchfound)
                return false;
-       strlcpy(key_line + 1, ConBuffer_GetLine(&history, history_line), sizeof(key_line) - 1);
+       dp_strlcpy(key_line + 1, ConBuffer_GetLine(&history, history_line), sizeof(key_line) - 1);
        key_linepos = (int)strlen(key_line);
        history_matchfound = false;
        return true;
@@ -136,7 +138,7 @@ static qbool Key_History_Get_foundCommand(void)
 static void Key_History_Up(void)
 {
        if(history_line == -1) // editing the "new" line
-               strlcpy(history_savedline, key_line + 1, sizeof(history_savedline));
+               dp_strlcpy(history_savedline, key_line + 1, sizeof(history_savedline));
 
        if (Key_History_Get_foundCommand())
                return;
@@ -146,14 +148,14 @@ static void Key_History_Up(void)
                history_line = CONBUFFER_LINES_COUNT(&history) - 1;
                if(history_line != -1)
                {
-                       strlcpy(key_line + 1, ConBuffer_GetLine(&history, history_line), sizeof(key_line) - 1);
+                       dp_strlcpy(key_line + 1, ConBuffer_GetLine(&history, history_line), sizeof(key_line) - 1);
                        key_linepos = (int)strlen(key_line);
                }
        }
        else if(history_line > 0)
        {
                --history_line; // this also does -1 -> 0, so it is good
-               strlcpy(key_line + 1, ConBuffer_GetLine(&history, history_line), sizeof(key_line) - 1);
+               dp_strlcpy(key_line + 1, ConBuffer_GetLine(&history, history_line), sizeof(key_line) - 1);
                key_linepos = (int)strlen(key_line);
        }
 }
@@ -169,12 +171,12 @@ static void Key_History_Down(void)
        if(history_line < CONBUFFER_LINES_COUNT(&history) - 1)
        {
                ++history_line;
-               strlcpy(key_line + 1, ConBuffer_GetLine(&history, history_line), sizeof(key_line) - 1);
+               dp_strlcpy(key_line + 1, ConBuffer_GetLine(&history, history_line), sizeof(key_line) - 1);
        }
        else
        {
                history_line = -1;
-               strlcpy(key_line + 1, history_savedline, sizeof(key_line) - 1);
+               dp_strlcpy(key_line + 1, history_savedline, sizeof(key_line) - 1);
        }
 
        key_linepos = (int)strlen(key_line);
@@ -183,12 +185,12 @@ static void Key_History_Down(void)
 static void Key_History_First(void)
 {
        if(history_line == -1) // editing the "new" line
-               strlcpy(history_savedline, key_line + 1, sizeof(history_savedline));
+               dp_strlcpy(history_savedline, key_line + 1, sizeof(history_savedline));
 
        if (CONBUFFER_LINES_COUNT(&history) > 0)
        {
                history_line = 0;
-               strlcpy(key_line + 1, ConBuffer_GetLine(&history, history_line), sizeof(key_line) - 1);
+               dp_strlcpy(key_line + 1, ConBuffer_GetLine(&history, history_line), sizeof(key_line) - 1);
                key_linepos = (int)strlen(key_line);
        }
 }
@@ -196,12 +198,12 @@ static void Key_History_First(void)
 static void Key_History_Last(void)
 {
        if(history_line == -1) // editing the "new" line
-               strlcpy(history_savedline, key_line + 1, sizeof(history_savedline));
+               dp_strlcpy(history_savedline, key_line + 1, sizeof(history_savedline));
 
        if (CONBUFFER_LINES_COUNT(&history) > 0)
        {
                history_line = CONBUFFER_LINES_COUNT(&history) - 1;
-               strlcpy(key_line + 1, ConBuffer_GetLine(&history, history_line), sizeof(key_line) - 1);
+               dp_strlcpy(key_line + 1, ConBuffer_GetLine(&history, history_line), sizeof(key_line) - 1);
                key_linepos = (int)strlen(key_line);
        }
 }
@@ -214,11 +216,11 @@ static void Key_History_Find_Backwards(void)
        size_t digits = strlen(va(vabuf, sizeof(vabuf), "%i", HIST_MAXLINES));
 
        if (history_line == -1) // editing the "new" line
-               strlcpy(history_savedline, key_line + 1, sizeof(history_savedline));
+               dp_strlcpy(history_savedline, key_line + 1, sizeof(history_savedline));
 
        if (strcmp(key_line + 1, history_searchstring)) // different string? Start a new search
        {
-               strlcpy(history_searchstring, key_line + 1, sizeof(history_searchstring));
+               dp_strlcpy(history_searchstring, key_line + 1, sizeof(history_searchstring));
                i = CONBUFFER_LINES_COUNT(&history) - 1;
        }
        else if (history_line == -1)
@@ -253,7 +255,7 @@ static void Key_History_Find_Forwards(void)
 
        if (strcmp(key_line + 1, history_searchstring)) // different string? Start a new search
        {
-               strlcpy(history_searchstring, key_line + 1, sizeof(history_searchstring));
+               dp_strlcpy(history_searchstring, key_line + 1, sizeof(history_searchstring));
                i = 0;
        }
        else i = history_line + 1;
@@ -694,7 +696,7 @@ Interactive line editing and console scrollback
 ====================
 */
 
-int chat_mode; // 0 for say, 1 for say_team, -1 for command
+signed char chat_mode; // 0 for say, 1 for say_team, -1 for command
 char chat_buffer[MAX_INPUTLINE];
 int chat_bufferpos = 0;
 
@@ -768,7 +770,7 @@ int Key_Parse_CommonKeys(cmd_state_t *cmd, qbool is_console, int key, int unicod
        if ((key == 'v' && KM_CTRL) || ((key == K_INS || key == K_KP_INS) && KM_SHIFT))
        {
                char *cbd, *p;
-               if ((cbd = Sys_GetClipboardData()) != 0)
+               if ((cbd = Sys_SDL_GetClipboardData()) != 0)
                {
                        int i;
 #if 1
@@ -949,7 +951,7 @@ int Key_Parse_CommonKeys(cmd_state_t *cmd, qbool is_console, int key, int unicod
                {
                        // hide ']' from u8_prevbyte otherwise it could go out of bounds
                        int newpos = (int)u8_prevbyte(line + linestart, linepos - linestart) + linestart;
-                       strlcpy(line + newpos, line + linepos, linesize + 1 - linepos);
+                       dp_strlcpy(line + newpos, line + linepos, linesize + 1 - linepos);
                        linepos = newpos;
                }
                return linepos;
@@ -1098,8 +1100,7 @@ static int Key_Convert_NumPadKey(int key)
        return key;
 }
 
-static void
-Key_Console(cmd_state_t *cmd, int key, int unicode)
+static void Key_Console(cmd_state_t *cmd, int key, int unicode)
 {
        int linepos;
 
@@ -1121,8 +1122,9 @@ Key_Console(cmd_state_t *cmd, int key, int unicode)
 
        if ((key == K_ENTER || key == K_KP_ENTER) && KM_NONE)
        {
-               Cbuf_AddText (cmd, key_line+1); // skip the ]
-               Cbuf_AddText (cmd, "\n");
+               // bones_was_here: prepending allows a loop such as `alias foo "bar; wait; foo"; foo`
+               // to be broken with an alias or unalias command
+               Cbuf_InsertText(cmd, key_line+1); // skip the ]
                Key_History_Push();
                key_linepos = Key_ClearEditLine(true);
                // force an update, because the command may take some time
@@ -1306,7 +1308,7 @@ Key_Message (cmd_state_t *cmd, int key, int ascii)
        if (key == K_ENTER || key == K_KP_ENTER || ascii == 10 || ascii == 13)
        {
                if(chat_mode < 0)
-                       Cmd_ExecuteString(cmd, chat_buffer, src_local, true); // not Cbuf_AddText to allow semiclons in args; however, this allows no variables then. Use aliases!
+                       Cmd_ExecuteString(cmd, chat_buffer, strlen(chat_buffer), src_local, true); // not Cbuf_AddText to allow semiclons in args; however, this allows no variables then. Use aliases!
                else
                        CL_ForwardToServer(va(vabuf, sizeof(vabuf), "%s %s", chat_mode ? "say_team" : "say ", chat_buffer));
 
@@ -1351,6 +1353,7 @@ the K_* names are matched up.
 int
 Key_StringToKeynum (const char *str)
 {
+       Uchar ch;
        const keyname_t  *kn;
 
        if (!str || !str[0])
@@ -1362,7 +1365,11 @@ Key_StringToKeynum (const char *str)
                if (!strcasecmp (str, kn->name))
                        return kn->keynum;
        }
-       return -1;
+
+       // non-ascii keys are Unicode codepoints, so give the character if it's valid;
+       // error message have more than one character, don't allow it
+       ch = u8_getnchar(str, &str, 3);
+       return (ch == 0 || *str != 0) ? -1 : (int)ch;
 }
 
 /*
@@ -1387,13 +1394,9 @@ Key_KeynumToString (int keynum, char *tinystr, size_t tinystrlength)
                        return kn->name;
 
        // if it is printable, output it as a single character
-       if (keynum > 32 && keynum < 256)
+       if (keynum > 32)
        {
-               if (tinystrlength >= 2)
-               {
-                       tinystr[0] = keynum;
-                       tinystr[1] = 0;
-               }
+               u8_fromchar(keynum, tinystr, tinystrlength);
                return tinystr;
        }
 
@@ -1513,9 +1516,9 @@ Key_In_Bind_f(cmd_state_t *cmd)
 // copy the rest of the command line
        line[0] = 0;                                                    // start out with a null string
        for (i = 3; i < c; i++) {
-               strlcat (line, Cmd_Argv(cmd, i), sizeof (line));
+               dp_strlcat (line, Cmd_Argv(cmd, i), sizeof (line));
                if (i != (c - 1))
-                       strlcat (line, " ", sizeof (line));
+                       dp_strlcat (line, " ", sizeof (line));
        }
 
        if(!Key_SetBinding (b, m, line))
@@ -1586,7 +1589,7 @@ static void
 Key_PrintBindList(int j)
 {
        char bindbuf[MAX_INPUTLINE];
-       char tinystr[2];
+       char tinystr[TINYSTR_LEN];
        const char *p;
        int i;
 
@@ -1597,9 +1600,9 @@ Key_PrintBindList(int j)
                {
                        Cmd_QuoteString(bindbuf, sizeof(bindbuf), p, "\"\\", false);
                        if (j == 0)
-                               Con_Printf("^2%s ^7= \"%s\"\n", Key_KeynumToString (i, tinystr, sizeof(tinystr)), bindbuf);
+                               Con_Printf("^2%s ^7= \"%s\"\n", Key_KeynumToString (i, tinystr, TINYSTR_LEN), bindbuf);
                        else
-                               Con_Printf("^3bindmap %d: ^2%s ^7= \"%s\"\n", j, Key_KeynumToString (i, tinystr, sizeof(tinystr)), bindbuf);
+                               Con_Printf("^3bindmap %d: ^2%s ^7= \"%s\"\n", j, Key_KeynumToString (i, tinystr, TINYSTR_LEN), bindbuf);
                }
        }
 }
@@ -1660,9 +1663,9 @@ Key_Bind_f(cmd_state_t *cmd)
 // copy the rest of the command line
        line[0] = 0;                                                    // start out with a null string
        for (i = 2; i < c; i++) {
-               strlcat (line, Cmd_Argv(cmd, i), sizeof (line));
+               dp_strlcat (line, Cmd_Argv(cmd, i), sizeof (line));
                if (i != (c - 1))
-                       strlcat (line, " ", sizeof (line));
+                       dp_strlcat (line, " ", sizeof (line));
        }
 
        if(!Key_SetBinding (b, 0, line))
@@ -1679,7 +1682,7 @@ Key_WriteBindings (qfile_t *f)
 {
        int         i, j;
        char bindbuf[MAX_INPUTLINE];
-       char tinystr[2];
+       char tinystr[TINYSTR_LEN];
        const char *p;
 
        // Override default binds
@@ -1694,9 +1697,9 @@ Key_WriteBindings (qfile_t *f)
                        {
                                Cmd_QuoteString(bindbuf, sizeof(bindbuf), p, "\"\\", false); // don't need to escape $ because cvars are not expanded inside bind
                                if (j == 0)
-                                       FS_Printf(f, "bind %s \"%s\"\n", Key_KeynumToString (i, tinystr, sizeof(tinystr)), bindbuf);
+                                       FS_Printf(f, "bind %s \"%s\"\n", Key_KeynumToString (i, tinystr, TINYSTR_LEN), bindbuf);
                                else
-                                       FS_Printf(f, "in_bind %d %s \"%s\"\n", j, Key_KeynumToString (i, tinystr, sizeof(tinystr)), bindbuf);
+                                       FS_Printf(f, "in_bind %d %s \"%s\"\n", j, Key_KeynumToString (i, tinystr, TINYSTR_LEN), bindbuf);
                        }
                }
        }
@@ -1970,14 +1973,14 @@ Key_Event (int key, int ascii, qbool down)
                        if(keydown[key] == 1 && down)
                        {
                                // button commands add keynum as a parm
+                               // prepend to avoid delays from `wait` commands added by other sources
                                if (bind[0] == '+')
-                                       Cbuf_AddText (cmd, va(vabuf, sizeof(vabuf), "%s %i\n", bind, key));
+                                       Cbuf_InsertText(cmd, va(vabuf, sizeof(vabuf), "%s %i\n", bind, key));
                                else
-                               {
-                                       Cbuf_AddText (cmd, bind);
-                                       Cbuf_AddText (cmd, "\n");
-                               }
-                       } else if(bind[0] == '+' && !down && keydown[key] == 0)
+                                       Cbuf_InsertText(cmd, bind);
+                       }
+                       else if(bind[0] == '+' && !down && keydown[key] == 0)
+                               // append -bind to ensure it's after the +bind in case they arrive in the same frame
                                Cbuf_AddText(cmd, va(vabuf, sizeof(vabuf), "-%s %i\n", bind + 1, key));
                }
                return;
@@ -2009,7 +2012,7 @@ Key_Event (int key, int ascii, qbool down)
        {
                if (down && con_closeontoggleconsole.integer && bind && !strncmp(bind, "toggleconsole", strlen("toggleconsole")) && ascii != STRING_COLOR_TAG)
                {
-                       Cbuf_AddText(cmd, "toggleconsole\n");  // Deferred to next frame so we're not sending the text event to the console.
+                       Cbuf_InsertText(cmd, "toggleconsole\n");  // Deferred to next frame so we're not sending the text event to the console.
                        tbl_keydest[key] = key_void; // key release should go nowhere (especially not to key_menu or key_game)
                        return;
                }
@@ -2051,14 +2054,14 @@ Key_Event (int key, int ascii, qbool down)
                                if(keydown[key] == 1 && down)
                                {
                                        // button commands add keynum as a parm
+                                       // prepend to avoid delays from `wait` commands added by other sources
                                        if (bind[0] == '+')
-                                               Cbuf_AddText (cmd, va(vabuf, sizeof(vabuf), "%s %i\n", bind, key));
+                                               Cbuf_InsertText(cmd, va(vabuf, sizeof(vabuf), "%s %i\n", bind, key));
                                        else
-                                       {
-                                               Cbuf_AddText (cmd, bind);
-                                               Cbuf_AddText (cmd, "\n");
-                                       }
-                               } else if(bind[0] == '+' && !down && keydown[key] == 0)
+                                               Cbuf_InsertText(cmd, bind);
+                               }
+                               else if(bind[0] == '+' && !down && keydown[key] == 0)
+                                       // append -bind to ensure it's after the +bind in case they arrive in the same frame
                                        Cbuf_AddText(cmd, va(vabuf, sizeof(vabuf), "-%s %i\n", bind + 1, key));
                        }
                        break;
diff --git a/keys.h b/keys.h
index f6b78cd0c841a385efea99dee7988d09c72530cd..2081e8009e21d9322df83943a5b834df86b8cf81 100644 (file)
--- a/keys.h
+++ b/keys.h
 #include "fs.h"
 #include "cmd.h"
 
+// the highest Unicode character to allow key binding.
+// note that an excessively high value may degrade fps
+// when code is looping through the bindings
+// U+ABFF is probably the highest bindable codepoint,
+// see: https://github.com/DarkPlacesEngine/darkplaces/pull/68#issuecomment-1416802873
+#define MAX_KEY_BINDS 0xAC00
+
+// how long is a "tinystr" to hold a keyboard key's
+// Unicode utf-8 presentation, plus final \x00
+// to allow all characters <= 0xffff, use 4
+#define TINYSTR_LEN 4
+
 //
 // these are the key numbers that should be passed to Key_Event
 //
@@ -353,7 +365,7 @@ typedef enum keynum_e
        K_MIDINOTE126,
        K_MIDINOTE127,
 
-       MAX_KEYS
+       MAX_KEYS = MAX_KEY_BINDS
 }
 keynum_t;
 
@@ -371,7 +383,7 @@ extern      keydest_t       key_dest;
 extern int                     key_consoleactive;
 extern char            *keybindings[MAX_BINDMAPS][MAX_KEYS];
 
-extern int chat_mode; // 0 for say, 1 for say_team, -1 for command
+extern signed char chat_mode; // 0 for say, 1 for say_team, -1 for command
 extern char chat_buffer[MAX_INPUTLINE];
 extern int     chat_bufferpos;
 
diff --git a/lhnet.c b/lhnet.c
index 97dc9b060487699240f24c99796a802561c9df69..e6a688de7c3fd67c4ee7108597da998dc947f284 100644 (file)
--- a/lhnet.c
+++ b/lhnet.c
@@ -2,13 +2,16 @@
 // Written by Ashley Rose Hale (LadyHavoc) 2003-06-15 and placed into public domain.
 
 #ifdef WIN32
-#ifdef _MSC_VER
-#pragma comment(lib, "ws2_32.lib")
-#endif
+# ifdef _MSC_VER
+#  pragma comment(lib, "ws2_32.lib")
+# endif
 # ifndef NOSUPPORTIPV6
 // Windows XP or higher is required for getaddrinfo, but the inclusion of wspiapi provides fallbacks for older versions
-# define _WIN32_WINNT 0x0501
+#  define _WIN32_WINNT 0x0501
 # endif
+// To increase FD_SETSIZE (defaults to 64 on Windows)
+// it must be defined before the first inclusion of winsock2.h
+# define FD_SETSIZE 1024 // Matches Linux and BSD defaults
 # include <winsock2.h>
 # include <ws2tcpip.h>
 # ifdef USE_WSPIAPI_H
@@ -550,8 +553,7 @@ int LHNETADDRESS_ToString(const lhnetaddress_t *vaddress, char *string, int stri
                {
                        if (stringbuffersize >= 12)
                        {
-                               dpsnprintf(string, stringbuffersize, "local:%d", address->port);
-                               return 1;
+                               return dpsnprintf(string, stringbuffersize, "local:%d", address->port);
                        }
                }
                else
@@ -559,7 +561,7 @@ int LHNETADDRESS_ToString(const lhnetaddress_t *vaddress, char *string, int stri
                        if (stringbuffersize >= 6)
                        {
                                memcpy(string, "local", 6);
-                               return 1;
+                               return 5;
                        }
                }
                break;
@@ -569,16 +571,14 @@ int LHNETADDRESS_ToString(const lhnetaddress_t *vaddress, char *string, int stri
                {
                        if (stringbuffersize >= 22)
                        {
-                               dpsnprintf(string, stringbuffersize, "%d.%d.%d.%d:%d", a[0], a[1], a[2], a[3], address->port);
-                               return 1;
+                               return dpsnprintf(string, stringbuffersize, "%d.%d.%d.%d:%d", a[0], a[1], a[2], a[3], address->port);
                        }
                }
                else
                {
                        if (stringbuffersize >= 16)
                        {
-                               dpsnprintf(string, stringbuffersize, "%d.%d.%d.%d", a[0], a[1], a[2], a[3]);
-                               return 1;
+                               return dpsnprintf(string, stringbuffersize, "%d.%d.%d.%d", a[0], a[1], a[2], a[3]);
                        }
                }
                break;
@@ -589,16 +589,14 @@ int LHNETADDRESS_ToString(const lhnetaddress_t *vaddress, char *string, int stri
                {
                        if (stringbuffersize >= 88)
                        {
-                               dpsnprintf(string, stringbuffersize, "[%x:%x:%x:%x:%x:%x:%x:%x]:%d", a[0] * 256 + a[1], a[2] * 256 + a[3], a[4] * 256 + a[5], a[6] * 256 + a[7], a[8] * 256 + a[9], a[10] * 256 + a[11], a[12] * 256 + a[13], a[14] * 256 + a[15], address->port);
-                               return 1;
+                               return dpsnprintf(string, stringbuffersize, "[%x:%x:%x:%x:%x:%x:%x:%x]:%d", a[0] * 256 + a[1], a[2] * 256 + a[3], a[4] * 256 + a[5], a[6] * 256 + a[7], a[8] * 256 + a[9], a[10] * 256 + a[11], a[12] * 256 + a[13], a[14] * 256 + a[15], address->port);
                        }
                }
                else
                {
                        if (stringbuffersize >= 80)
                        {
-                               dpsnprintf(string, stringbuffersize, "%x:%x:%x:%x:%x:%x:%x:%x", a[0] * 256 + a[1], a[2] * 256 + a[3], a[4] * 256 + a[5], a[6] * 256 + a[7], a[8] * 256 + a[9], a[10] * 256 + a[11], a[12] * 256 + a[13], a[14] * 256 + a[15]);
-                               return 1;
+                               return dpsnprintf(string, stringbuffersize, "%x:%x:%x:%x:%x:%x:%x:%x", a[0] * 256 + a[1], a[2] * 256 + a[3], a[4] * 256 + a[5], a[6] * 256 + a[7], a[8] * 256 + a[9], a[10] * 256 + a[11], a[12] * 256 + a[13], a[14] * 256 + a[15]);
                        }
                }
                break;
@@ -607,14 +605,6 @@ int LHNETADDRESS_ToString(const lhnetaddress_t *vaddress, char *string, int stri
        return 0;
 }
 
-int LHNETADDRESS_GetAddressType(const lhnetaddress_t *address)
-{
-       if (address)
-               return address->addresstype;
-       else
-               return LHNETADDRESSTYPE_NONE;
-}
-
 const char *LHNETADDRESS_GetInterfaceName(const lhnetaddress_t *vaddress, char *ifname, size_t ifnamelength)
 {
 #ifndef NOSUPPORTIPV6
@@ -724,7 +714,7 @@ typedef struct lhnetpacket_s
 lhnetpacket_t;
 
 static int lhnet_active;
-static lhnetsocket_t lhnet_socketlist;
+lhnetsocket_t lhnet_socketlist;
 static lhnetpacket_t lhnet_packetlist;
 static int lhnet_default_dscp = 0;
 #ifdef WIN32
@@ -839,36 +829,6 @@ static const char *LHNETPRIVATE_StrError(void)
 #endif
 }
 
-void LHNET_SleepUntilPacket_Microseconds(int microseconds)
-{
-#ifdef FD_SET
-       fd_set fdreadset;
-       struct timeval tv;
-       int lastfd;
-       lhnetsocket_t *s;
-       FD_ZERO(&fdreadset);
-       lastfd = 0;
-       List_For_Each_Entry(s, &lhnet_socketlist.list, lhnetsocket_t, list)
-       {
-               if (s->address.addresstype == LHNETADDRESSTYPE_INET4 || s->address.addresstype == LHNETADDRESSTYPE_INET6)
-               {
-                       if (lastfd < s->inetsocket)
-                               lastfd = s->inetsocket;
-#if defined(WIN32) && !defined(_MSC_VER)
-                       FD_SET((int)s->inetsocket, &fdreadset);
-#else
-                       FD_SET((unsigned int)s->inetsocket, &fdreadset);
-#endif
-               }
-       }
-       tv.tv_sec = microseconds / 1000000;
-       tv.tv_usec = microseconds % 1000000;
-       select(lastfd + 1, &fdreadset, NULL, NULL, &tv);
-#else
-       Sys_Sleep(microseconds);
-#endif
-}
-
 lhnetsocket_t *LHNET_OpenSocket_Connectionless(lhnetaddress_t *address)
 {
        lhnetsocket_t *lhnetsocket, *s;
diff --git a/lhnet.h b/lhnet.h
index 6d1d1f3f22c08f661d3c4ef40dc8da454f682e3a..d368940dbf2a04d79eb2243a4f18acda48f8cbce 100644 (file)
--- a/lhnet.h
+++ b/lhnet.h
@@ -26,8 +26,15 @@ lhnetaddress_t;
 
 int LHNETADDRESS_FromPort(lhnetaddress_t *address, lhnetaddresstype_t addresstype, int port);
 int LHNETADDRESS_FromString(lhnetaddress_t *address, const char *string, int defaultport);
+/// Returns the number of bytes written to *string excluding the \0 terminator.
 int LHNETADDRESS_ToString(const lhnetaddress_t *address, char *string, int stringbuffersize, int includeport);
-int LHNETADDRESS_GetAddressType(const lhnetaddress_t *address);
+static inline lhnetaddresstype_t LHNETADDRESS_GetAddressType(const lhnetaddress_t *address)
+{
+       if (address)
+               return address->addresstype;
+       else
+               return LHNETADDRESSTYPE_NONE;
+}
 const char *LHNETADDRESS_GetInterfaceName(const lhnetaddress_t *address, char *ifname, size_t ifnamelength);
 int LHNETADDRESS_GetPort(const lhnetaddress_t *address);
 int LHNETADDRESS_SetPort(lhnetaddress_t *address, int port);
@@ -40,6 +47,7 @@ typedef struct lhnetsocket_s
        llist_t list;
 }
 lhnetsocket_t;
+extern lhnetsocket_t lhnet_socketlist;
 
 void LHNET_Init(void);
 void LHNET_Shutdown(void);
index 26cb7a4914de0cc915f19fa813d73fe056d80422..d4b2defce063377b6827b735a03bb0dd11bb988b 100644 (file)
--- a/libcurl.c
+++ b/libcurl.c
@@ -7,14 +7,17 @@
 #include "jpeg.h"
 #include "image_png.h"
 
-static cvar_t cl_curl_maxdownloads = {CF_CLIENT | CF_ARCHIVE, "cl_curl_maxdownloads","1", "maximum number of concurrent HTTP/FTP downloads"};
-static cvar_t cl_curl_maxspeed = {CF_CLIENT | CF_ARCHIVE, "cl_curl_maxspeed","300", "maximum download speed (KiB/s)"};
-static cvar_t sv_curl_defaulturl = {CF_SERVER | CF_ARCHIVE, "sv_curl_defaulturl","", "default autodownload source URL"};
-static cvar_t sv_curl_serverpackages = {CF_SERVER | CF_ARCHIVE, "sv_curl_serverpackages","", "list of required files for the clients, separated by spaces"};
-static cvar_t sv_curl_maxspeed = {CF_SERVER | CF_ARCHIVE, "sv_curl_maxspeed","0", "maximum download speed for clients downloading from sv_curl_defaulturl (KiB/s)"};
-static cvar_t cl_curl_enabled = {CF_CLIENT | CF_ARCHIVE, "cl_curl_enabled","1", "whether client's download support is enabled"};
-static cvar_t cl_curl_useragent = {CF_CLIENT, "cl_curl_useragent","1", "send the User-Agent string (note: turning this off may break stuff)"};
-static cvar_t cl_curl_useragent_append = {CF_CLIENT, "cl_curl_useragent_append","", "a string to append to the User-Agent string (useful for name and version number of your mod)"};
+static cvar_t curl_enabled = {CF_SHARED | CF_ARCHIVE, "curl_enabled","1", "whether libcurl may be used to GET files or POST data"};
+static cvar_t curl_maxdownloads = {CF_SHARED | CF_ARCHIVE, "curl_maxdownloads","3", "maximum number of concurrent HTTP/FTP downloads"};
+static cvar_t curl_maxspeed = {CF_SHARED | CF_ARCHIVE, "curl_maxspeed","0", "maximum download speed (KiB/s)"};
+static cvar_t curl_useragent = {CF_SHARED, "curl_useragent","1", "send the User-Agent string (note: turning this off may break stuff)"};
+static cvar_t curl_useragent_append = {CF_SHARED, "curl_useragent_append","", "a string to append to the User-Agent string (useful for name and version number of your mod)"};
+
+static cvar_t sv_curl_defaulturl = {CF_SERVER, "sv_curl_defaulturl","", "default autodownload source URL"};
+static cvar_t sv_curl_serverpackages = {CF_SERVER, "sv_curl_serverpackages","", "list of required files for the clients, separated by spaces"};
+static cvar_t sv_curl_maxspeed = {CF_SERVER, "sv_curl_maxspeed","0", "maximum download speed for clients downloading from sv_curl_defaulturl (KiB/s)"};
+
+static cvar_t developer_curl = {CF_SHARED, "developer_curl","0", "whether verbose libcurl output should be printed to stderr"};
 
 /*
 =================================================================
@@ -62,6 +65,7 @@ typedef enum
        CINIT(LOW_SPEED_TIME, LONG, 20),
        CINIT(RESUME_FROM, LONG, 21),
        CINIT(HTTPHEADER, OBJECTPOINT, 23),
+       CINIT(VERBOSE, LONG, 41),
        CINIT(POST, LONG, 47),         /* HTTP POST method */
        CINIT(FOLLOWLOCATION, LONG, 52),  /* use Location: Luke! */
        CINIT(POSTFIELDSIZE, LONG, 60),
@@ -156,6 +160,7 @@ static const char * (*qcurl_easy_strerror) (CURLcode);
 
 static CURLM * (*qcurl_multi_init) (void);
 static CURLMcode (*qcurl_multi_perform) (CURLM *multi_handle, int *running_handles);
+static CURLMcode (*qcurl_multi_wait) (CURLM *multi_handle, void*, unsigned int extra_nfds, int timeout_ms, int *ret);
 static CURLMcode (*qcurl_multi_add_handle) (CURLM *multi_handle, CURL *easy_handle);
 static CURLMcode (*qcurl_multi_remove_handle) (CURLM *multi_handle, CURL *easy_handle);
 static CURLMsg * (*qcurl_multi_info_read) (CURLM *multi_handle, int *msgs_in_queue);
@@ -175,6 +180,7 @@ static dllfunction_t curlfuncs[] =
        {"curl_easy_getinfo",           (void **) &qcurl_easy_getinfo},
        {"curl_multi_init",                     (void **) &qcurl_multi_init},
        {"curl_multi_perform",          (void **) &qcurl_multi_perform},
+       {"curl_multi_wait",             (void **) &qcurl_multi_wait},
        {"curl_multi_add_handle",       (void **) &qcurl_multi_add_handle},
        {"curl_multi_remove_handle",(void **) &qcurl_multi_remove_handle},
        {"curl_multi_info_read",        (void **) &qcurl_multi_info_read},
@@ -250,7 +256,7 @@ static void Curl_CommandWhenDone(const char *cmd)
        if(!curl_dll)
                return;
        if(cmd)
-               strlcpy(command_when_done, cmd, sizeof(command_when_done));
+               dp_strlcpy(command_when_done, cmd, sizeof(command_when_done));
        else
                *command_when_done = 0;
 }
@@ -266,7 +272,7 @@ static void Curl_CommandWhenError(const char *cmd)
        if(!curl_dll)
                return;
        if(cmd)
-               strlcpy(command_when_error, cmd, sizeof(command_when_error));
+               dp_strlcpy(command_when_error, cmd, sizeof(command_when_error));
        else
                *command_when_error = 0;
 }
@@ -430,6 +436,8 @@ static size_t CURL_fwrite(void *data, size_t size, size_t nmemb, void *vdi)
 
        di->bytes_received += bytes;
 
+       //Con_Printf("CURL_fwrite callback timestamp: %f bytes: %ld\n", host.realtime, ret);
+
        return ret;
        // Why not ret / nmemb?
        // Because CURLOPT_WRITEFUNCTION docs say to return the number of bytes.
@@ -545,7 +553,7 @@ static void Curl_EndDownload(downloadinfo *di, CurlStatus status, CURLcode error
                        break;
        }
        if(content_type_)
-               strlcpy(content_type, content_type_, sizeof(content_type));
+               dp_strlcpy(content_type, content_type_, sizeof(content_type));
        else
                *content_type = 0;
 
@@ -581,7 +589,7 @@ static void Curl_EndDownload(downloadinfo *di, CurlStatus status, CURLcode error
 
        if(ok && di->loadtype == LOADTYPE_PAK)
        {
-               ok = FS_AddPack(di->filename, NULL, true);
+               ok = FS_AddPack(di->filename, NULL, true, true);
                if(!ok)
                        CLEAR_AND_RETRY();
        }
@@ -679,7 +687,7 @@ static void CheckPendingDownloads(void)
        char vabuf[1024];
        if(!curl_dll)
                return;
-       if(numdownloads < cl_curl_maxdownloads.integer)
+       if(numdownloads < curl_maxdownloads.integer)
        {
                downloadinfo *di;
                List_For_Each_Entry(di, &downloads, downloadinfo, list)
@@ -713,7 +721,7 @@ static void CheckPendingDownloads(void)
                                di->curle = qcurl_easy_init();
                                di->slist = NULL;
                                qcurl_easy_setopt(di->curle, CURLOPT_URL, di->url);
-                               if(cl_curl_useragent.integer)
+                               if(curl_useragent.integer)
                                {
                                        const char *ua
 #ifdef HTTP_USER_AGENT
@@ -723,17 +731,19 @@ static void CheckPendingDownloads(void)
 #endif
                                        if(!ua)
                                                ua = "";
-                                       if(*cl_curl_useragent_append.string)
+                                       if(*curl_useragent_append.string)
                                                ua = va(vabuf, sizeof(vabuf), "%s%s%s",
                                                        ua,
                                                        (ua[0] && ua[strlen(ua)-1] != ' ')
                                                                ? " "
                                                                : "",
-                                                       cl_curl_useragent_append.string);
+                                                       curl_useragent_append.string);
                                        qcurl_easy_setopt(di->curle, CURLOPT_USERAGENT, ua);
                                }
                                else
                                        qcurl_easy_setopt(di->curle, CURLOPT_USERAGENT, "");
+                               if(developer_curl.integer) 
+                                       qcurl_easy_setopt(di->curle, CURLOPT_VERBOSE, (long) 1);
                                qcurl_easy_setopt(di->curle, CURLOPT_REFERER, di->referer);
                                qcurl_easy_setopt(di->curle, CURLOPT_RESUME_FROM, (long) di->startpos);
                                qcurl_easy_setopt(di->curle, CURLOPT_FOLLOWLOCATION, 1);
@@ -782,7 +792,7 @@ static void CheckPendingDownloads(void)
                                qcurl_multi_add_handle(curlm, di->curle);
                                di->started = true;
                                ++numdownloads;
-                               if(numdownloads >= cl_curl_maxdownloads.integer)
+                               if(numdownloads >= curl_maxdownloads.integer)
                                        break;
                        }
                }
@@ -803,7 +813,7 @@ void Curl_Init(void)
        if(!curl_dll)
                return;
        if (Thread_HasThreads()) curl_mutex = Thread_CreateMutex();
-       qcurl_global_init(CURL_GLOBAL_NOTHING);
+       qcurl_global_init(CURL_GLOBAL_SSL);
        curlm = qcurl_multi_init();
 }
 
@@ -826,6 +836,12 @@ void Curl_Shutdown(void)
        curl_dll = NULL;
 }
 
+// for VM_checkextension()
+qbool Curl_Available(void)
+{
+       return curl_dll ? true : false;
+}
+
 /*
 ====================
 Curl_Find
@@ -873,7 +889,7 @@ static qbool Curl_Begin(const char *URL, const char *extraheaders, double maxspe
                if(loadtype != LOADTYPE_NONE)
                        Host_Error("Curl_Begin: loadtype and buffer are both set");
 
-       if(!curl_dll || !cl_curl_enabled.integer)
+       if(!curl_dll || !curl_enabled.integer)
        {
                return false;
        }
@@ -982,7 +998,7 @@ static qbool Curl_Begin(const char *URL, const char *extraheaders, double maxspe
                                if(loadtype == LOADTYPE_PAK)
                                {
                                        qbool already_loaded;
-                                       if(FS_AddPack(fn, &already_loaded, true))
+                                       if(FS_AddPack(fn, &already_loaded, true, true))
                                        {
                                                Con_DPrintf("%s already exists, not downloading!\n", fn);
                                                if(already_loaded)
@@ -1045,8 +1061,8 @@ static qbool Curl_Begin(const char *URL, const char *extraheaders, double maxspe
                if(forthismap)
                        ++numdownloads_added;
                di = (downloadinfo *) Z_Malloc(sizeof(*di));
-               strlcpy(di->filename, name, sizeof(di->filename));
-               strlcpy(di->url, URL, sizeof(di->url));
+               dp_strlcpy(di->filename, name, sizeof(di->filename));
+               dp_strlcpy(di->url, URL, sizeof(di->url));
                dpsnprintf(di->referer, sizeof(di->referer), "dp://%s/", cls.netcon ? cls.netcon->address : "notconnected.invalid");
                di->forthismap = forthismap;
                di->stream = NULL;
@@ -1122,7 +1138,7 @@ void Curl_Frame(void)
 
        noclear = false;
 
-       if(!cl_curl_enabled.integer)
+       if(!curl_enabled.integer && cls.state != ca_dedicated)
                return;
 
        if(!curl_dll)
@@ -1212,7 +1228,7 @@ void Curl_Frame(void)
 
        // use the slowest allowing download to derive the maxspeed... this CAN
        // be done better, but maybe later
-       maxspeed = cl_curl_maxspeed.value;
+       maxspeed = curl_maxspeed.value;
        List_For_Each_Entry(di, &downloads, downloadinfo, list)
                if(di->maxspeed > 0)
                        if(di->maxspeed < maxspeed || maxspeed <= 0)
@@ -1231,6 +1247,34 @@ void Curl_Frame(void)
        if (curl_mutex) Thread_UnlockMutex(curl_mutex);
 }
 
+/*
+====================
+Curl_Select
+
+Sleeps until there's some transfer progress or a timeout is reached,
+unfortunately the timeout is only in milliseconds.
+This allows good throughput even at very low FPS.
+Less important on newer libcurl versions but still helps.
+
+Returns 0 immediately if there's no transfers to wait for,
+or > 0 if a transfer is ready or the timeout was reached.
+====================
+*/
+int Curl_Select(int timeout_ms)
+{
+       CURLMcode err;
+       int numfds;
+
+       if (List_Is_Empty(&downloads))
+               return 0;
+
+       err = qcurl_multi_wait(curlm, NULL, 0, timeout_ms, &numfds);
+       if (err == CURLM_OK)
+               return numfds;
+       Con_Printf("curl_multi_wait() failed, code %d\n", err);
+       return 0;
+}
+
 /*
 ====================
 Curl_CancelAll
@@ -1394,7 +1438,7 @@ static void Curl_Curl_f(cmd_state_t *cmd)
                return;
        }
 
-       if(!cl_curl_enabled.integer)
+       if(!curl_enabled.integer)
        {
                Con_Print("curl support not enabled. Set cl_curl_enabled to 1 to enable.\n");
                return;
@@ -1534,14 +1578,23 @@ loads the commands and cvars this library uses
 */
 void Curl_Init_Commands(void)
 {
-       Cvar_RegisterVariable (&cl_curl_enabled);
-       Cvar_RegisterVariable (&cl_curl_maxdownloads);
-       Cvar_RegisterVariable (&cl_curl_maxspeed);
+       Cvar_RegisterVariable (&curl_enabled);
+       Cvar_RegisterVariable (&curl_maxdownloads);
+       Cvar_RegisterVariable (&curl_maxspeed);
+       Cvar_RegisterVariable (&curl_useragent);
+       Cvar_RegisterVariable (&curl_useragent_append);
+       Cvar_RegisterVirtual  (&curl_enabled,          "cl_curl_enabled");
+       Cvar_RegisterVirtual  (&curl_maxdownloads,     "cl_curl_maxdownloads");
+       Cvar_RegisterVirtual  (&curl_maxspeed,         "cl_curl_maxspeed");
+       Cvar_RegisterVirtual  (&curl_useragent,        "cl_curl_useragent");
+       Cvar_RegisterVirtual  (&curl_useragent_append, "cl_curl_useragent_append");
+
        Cvar_RegisterVariable (&sv_curl_defaulturl);
        Cvar_RegisterVariable (&sv_curl_serverpackages);
        Cvar_RegisterVariable (&sv_curl_maxspeed);
-       Cvar_RegisterVariable (&cl_curl_useragent);
-       Cvar_RegisterVariable (&cl_curl_useragent_append);
+
+       Cvar_RegisterVariable (&developer_curl);
+
        Cmd_AddCommand(CF_CLIENT | CF_CLIENT_FROM_SERVER, "curl", Curl_Curl_f, "download data from an URL and add to search path");
        //Cmd_AddCommand(cmd_local, "curlcat", Curl_CurlCat_f, "display data from an URL (debugging command)");
 }
@@ -1585,7 +1638,7 @@ Curl_downloadinfo_t *Curl_GetDownloadInfo(int *nDownloads, const char **addition
                if(developer.integer <= 0)
                        if(di->buffer)
                                continue;
-               strlcpy(downinfo[i].filename, di->filename, sizeof(downinfo[i].filename));
+               dp_strlcpy(downinfo[i].filename, di->filename, sizeof(downinfo[i].filename));
                if(di->curle)
                {
                        downinfo[i].progress = Curl_GetDownloadAmount(di);
@@ -1676,7 +1729,7 @@ static const char *Curl_FindPackURL(const char *filename)
                                                *urlend = 0;
                                                if(matchpattern(filename, pattern, true))
                                                {
-                                                       strlcpy(foundurl, url, sizeof(foundurl));
+                                                       dp_strlcpy(foundurl, url, sizeof(foundurl));
                                                        Z_Free(buf);
                                                        return foundurl;
                                                }
@@ -1728,7 +1781,7 @@ void Curl_RequireFile(const char *filename)
 {
        requirement *req = (requirement *) Z_Malloc(sizeof(*requirements));
        req->next = requirements;
-       strlcpy(req->filename, filename, sizeof(req->filename));
+       dp_strlcpy(req->filename, filename, sizeof(req->filename));
        requirements = req;
 }
 
@@ -1780,18 +1833,18 @@ static qbool Curl_SendRequirement(const char *filename, qbool foundone, char *se
        if(packurl && *packurl && strcmp(packurl, "-"))
        {
                if(!foundone)
-                       strlcat(sendbuffer, "curl --clear_autodownload\n", sendbuffer_len);
+                       dp_strlcat(sendbuffer, "curl --clear_autodownload\n", sendbuffer_len);
 
-               strlcat(sendbuffer, "curl --pak --forthismap --as ", sendbuffer_len);
-               strlcat(sendbuffer, thispack, sendbuffer_len);
+               dp_strlcat(sendbuffer, "curl --pak --forthismap --as ", sendbuffer_len);
+               dp_strlcat(sendbuffer, thispack, sendbuffer_len);
                if(sv_curl_maxspeed.value > 0)
                        dpsnprintf(sendbuffer + strlen(sendbuffer), sendbuffer_len - strlen(sendbuffer), " --maxspeed=%.1f", sv_curl_maxspeed.value);
-               strlcat(sendbuffer, " --for ", sendbuffer_len);
-               strlcat(sendbuffer, filename, sendbuffer_len);
-               strlcat(sendbuffer, " ", sendbuffer_len);
-               strlcat(sendbuffer, packurl, sendbuffer_len);
-               strlcat(sendbuffer, thispack, sendbuffer_len);
-               strlcat(sendbuffer, "\n", sendbuffer_len);
+               dp_strlcat(sendbuffer, " --for ", sendbuffer_len);
+               dp_strlcat(sendbuffer, filename, sendbuffer_len);
+               dp_strlcat(sendbuffer, " ", sendbuffer_len);
+               dp_strlcat(sendbuffer, packurl, sendbuffer_len);
+               dp_strlcat(sendbuffer, thispack, sendbuffer_len);
+               dp_strlcat(sendbuffer, "\n", sendbuffer_len);
 
                return true;
        }
@@ -1814,7 +1867,7 @@ void Curl_SendRequirements(void)
                foundone = Curl_SendRequirement(com_token, foundone, sendbuffer, sizeof(sendbuffer)) || foundone;
 
        if(foundone)
-               strlcat(sendbuffer, "curl --finish_autodownload\n", sizeof(sendbuffer));
+               dp_strlcat(sendbuffer, "curl --finish_autodownload\n", sizeof(sendbuffer));
 
        if(strlen(sendbuffer) + 1 < sizeof(sendbuffer))
                SV_ClientCommands("%s", sendbuffer);
index d960be8af0939c7c97eb19293e5bbe4657b75ecd..908bc4828f6c9b49c65dc0f94cece728cc986b14 100644 (file)
--- a/libcurl.h
+++ b/libcurl.h
@@ -14,6 +14,7 @@ typedef void (*curl_callback_t) (int status, size_t length_received, unsigned ch
 // code is one of the CURLCBSTATUS constants, or the HTTP error code (when > 0).
 
 void Curl_Frame(void);
+int Curl_Select(int timeout_ms);
 qbool Curl_Running(void);
 qbool Curl_Begin_ToFile(const char *URL, double maxspeed, const char *name, int loadtype, qbool forthismap);
 
@@ -24,6 +25,7 @@ void Curl_Cancel_ToMemory(curl_callback_t callback, void* cbdata);
 void Curl_Init(void);
 void Curl_Init_Commands(void);
 void Curl_Shutdown(void);
+qbool Curl_Available(void);
 void Curl_CancelAll(void);
 void Curl_Clear_forthismap(void);
 qbool Curl_Have_forthismap(void);
index 064a6e7b99d41c8cd387a8f981cad3ef143048e4..b62194cd42dc9a93e07649c33aec7b33817f054b 100644 (file)
--- a/makefile
+++ b/makefile
@@ -29,21 +29,27 @@ endif  # ifndef DP_MAKE_TARGET
 # If we're targeting an x86 CPU we want to enable DP_SSE (CFLAGS_SSE and SSE2)
 ifeq ($(DP_MAKE_TARGET), mingw)
        DP_SSE:=1
+else ifeq ($(OS),Windows_NT)
+       ifeq ($(PROCESSOR_ARCHITECTURE),AMD64)
+               DP_SSE:=1
+       else ifeq ($(PROCESSOR_ARCHITEW6432),AMD64)
+               DP_SSE:=1
+       else ifeq ($(PROCESSOR_ARCHITECTURE),x86)
+               DP_SSE:=1
+       else
+               DP_SSE:=0
+       endif
 else
        DP_MACHINE:=$(shell uname -m)
        ifeq ($(DP_MACHINE),x86_64)
                DP_SSE:=1
-       else
-       ifeq ($(DP_MACHINE),i686)
+       else ifeq ($(DP_MACHINE),i686)
                DP_SSE:=1
-       else
-       ifeq ($(DP_MACHINE),i386)
+       else ifeq ($(DP_MACHINE),i386)
                DP_SSE:=1
        else
                DP_SSE:=0
-       endif # ifeq ($(DP_MACHINE),i386)
-       endif # ifeq ($(DP_MACHINE),i686)
-       endif # ifeq ($(DP_MACHINE),x86_64)
+       endif
 endif
 
 # Makefile name
@@ -60,13 +66,6 @@ else
        CMD_MKDIR=$(CMD_UNIXMKDIR)
 endif
 
-# 64bits AMD CPUs use another lib directory
-ifeq ($(DP_MACHINE),x86_64)
-       UNIX_X11LIBPATH:=/usr/X11R6/lib64
-else
-       UNIX_X11LIBPATH:=/usr/X11R6/lib
-endif
-
 # default targets
 TARGETS_DEBUG=sv-debug sdl-debug
 TARGETS_PROFILE=sv-profile sdl-profile
@@ -74,6 +73,7 @@ TARGETS_RELEASE=sv-release sdl-release
 TARGETS_RELEASE_PROFILE=sv-release-profile sdl-release-profile
 TARGETS_NEXUIZ=sv-nexuiz sdl-nexuiz
 
+
 ###### Optional features #####
 DP_VIDEO_CAPTURE?=enabled
 ifeq ($(DP_VIDEO_CAPTURE), enabled)
@@ -139,8 +139,6 @@ ifeq ($(DP_MAKE_TARGET), macosx)
        DP_LINK_CRYPTO_RIJNDAEL?=dlopen
        DP_LINK_XMP?=dlopen
 
-       # on OS X, we don't build the CL by default because it uses deprecated
-       # and not-implemented-in-64bit Carbon
        TARGETS_DEBUG=sv-debug sdl-debug
        TARGETS_PROFILE=sv-profile sdl-profile
        TARGETS_RELEASE=sv-release sdl-release
@@ -204,26 +202,6 @@ ifeq ($(DP_MAKE_TARGET), bsd)
 endif
 
 # Win32 configuration
-ifeq ($(WIN32RELEASE), 1)
-#      TARGET=i686-pc-mingw32
-#      CC=$(TARGET)-g++
-#      WINDRES=$(TARGET)-windres
-       CPUOPTIMIZATIONS=-march=pentium3 -mfpmath=sse -fno-math-errno -fno-rounding-math -fno-signaling-nans -fno-trapping-math
-#       CPUOPTIMIZATIONS+=-DUSE_WSPIAPI_H -DSUPPORTIPV6
-       LDFLAGS_WINCOMMON=-Wl,--large-address-aware
-else
-       LDFLAGS_WINCOMMON=
-endif
-
-ifeq ($(WIN64RELEASE), 1)
-#      TARGET=x86_64-pc-mingw32
-#      CC=$(TARGET)-g++
-#      WINDRES=$(TARGET)-windres
-endif
-
-CFLAGS_WARNINGS=-Wall -Winline -Werror=c++-compat -Wwrite-strings -Wshadow -Wold-style-definition -Wstrict-prototypes -Wsign-compare -Wdeclaration-after-statement -Wmissing-prototypes
-
-
 ifeq ($(DP_MAKE_TARGET), mingw)
        OBJ_ICON=darkplaces.o
        OBJ_ICON_NEXUIZ=nexuiz.o
@@ -249,20 +227,35 @@ ifeq ($(DP_MAKE_TARGET), mingw)
        DP_LINK_XMP?=dlopen
 endif
 
-# set these to "" if you want to use dynamic loading instead
-# zlib
+
+##### Library linking #####
+# SDL2
+SDL_CONFIG?=sdl2-config
+SDLCONFIG_UNIXCFLAGS?=`$(SDL_CONFIG) --cflags`
+SDLCONFIG_UNIXCFLAGS_X11?=
+SDLCONFIG_UNIXLIBS?=`$(SDL_CONFIG) --libs`
+SDLCONFIG_UNIXLIBS_X11?=-lX11
+SDLCONFIG_UNIXSTATICLIBS?=`$(SDL_CONFIG) --static-libs`
+SDLCONFIG_UNIXSTATICLIBS_X11?=-lX11
+SDLCONFIG_MACOSXCFLAGS=$(SDLCONFIG_UNIXCFLAGS)
+SDLCONFIG_MACOSXLIBS=$(SDLCONFIG_UNIXLIBS)
+SDLCONFIG_MACOSXSTATICLIBS=$(SDLCONFIG_UNIXSTATICLIBS)
 ifeq ($(DP_LINK_SDL), shared)
        SDL_LIBS=$(SDLCONFIG_LIBS)
-endif
-ifeq ($(DP_LINK_SDL), static)
+else ifeq ($(DP_LINK_SDL), static)
        SDL_LIBS=$(SDLCONFIG_STATICLIBS)
+else ifeq ($(DP_LINK_SDL), dlopen)
+  $(error libSDL2 can only be used with shared or static linking)
 endif
 
+# zlib
 ifeq ($(DP_LINK_ZLIB), shared)
        CFLAGS_LIBZ=-DLINK_TO_ZLIB
        LIB_Z=-lz
-endif
-ifeq ($(DP_LINK_ZLIB), dlopen)
+else ifeq ($(DP_LINK_ZLIB), static)
+       CFLAGS_LIBZ=-DLINK_TO_ZLIB
+       LIB_Z=-l:libz.a
+else ifeq ($(DP_LINK_ZLIB), dlopen)
        CFLAGS_LIBZ=
        LIB_Z=
 endif
@@ -271,8 +264,10 @@ endif
 ifeq ($(DP_LINK_JPEG), shared)
        CFLAGS_LIBJPEG=-DLINK_TO_LIBJPEG
        LIB_JPEG=-ljpeg
-endif
-ifeq ($(DP_LINK_JPEG), dlopen)
+else ifeq ($(DP_LINK_JPEG), static)
+       CFLAGS_LIBJPEG=-DLINK_TO_LIBJPEG
+       LIB_JPEG=-l:libjpeg.a
+else ifeq ($(DP_LINK_JPEG), dlopen)
        CFLAGS_LIBJPEG=
        LIB_JPEG=
 endif
@@ -282,8 +277,12 @@ ifeq ($(DP_LINK_ODE), shared)
        ODE_CONFIG?=ode-config
        LIB_ODE=`$(ODE_CONFIG) --libs`
        CFLAGS_ODE=`$(ODE_CONFIG) --cflags` -DUSEODE -DLINK_TO_LIBODE
-endif
-ifeq ($(DP_LINK_ODE), dlopen)
+else ifeq ($(DP_LINK_ODE), static)
+       # This is the configuration from Xonotic
+       ODE_CONFIG?=ode-config
+       LIB_ODE=-l:libode.a -lstdc++ -pthread
+       CFLAGS_ODE=-DUSEODE -DLINK_TO_LIBODE -DdDOUBLE
+else ifeq ($(DP_LINK_ODE), dlopen)
        LIB_ODE=
        CFLAGS_ODE=-DUSEODE
 endif
@@ -292,16 +291,25 @@ endif
 ifeq ($(DP_LINK_CRYPTO), shared)
        LIB_CRYPTO=-ld0_blind_id
        CFLAGS_CRYPTO=-DLINK_TO_CRYPTO
-endif
-ifeq ($(DP_LINK_CRYPTO), dlopen)
+else ifeq ($(DP_LINK_CRYPTO), static)
+       LIB_CRYPTO=-l:libd0_blind_id.a -lgmp
+       CFLAGS_CRYPTO=-DLINK_TO_CRYPTO
+else ifeq ($(DP_LINK_CRYPTO), static_inc_gmp)
+       LIB_CRYPTO=-l:libd0_blind_id.a -l:libgmp.a
+       CFLAGS_CRYPTO=-DLINK_TO_CRYPTO
+else ifeq ($(DP_LINK_CRYPTO), dlopen)
        LIB_CRYPTO=
        CFLAGS_CRYPTO=
 endif
+
+# d0_rijndael
 ifeq ($(DP_LINK_CRYPTO_RIJNDAEL), shared)
        LIB_CRYPTO_RIJNDAEL=-ld0_rijndael
        CFLAGS_CRYPTO_RIJNDAEL=-DLINK_TO_CRYPTO_RIJNDAEL
-endif
-ifeq ($(DP_LINK_CRYPTO_RIJNDAEL), dlopen)
+else ifeq ($(DP_LINK_CRYPTO_RIJNDAEL), static)
+       LIB_CRYPTO_RIJNDAEL=-l:libd0_rijndael.a
+       CFLAGS_CRYPTO_RIJNDAEL=-DLINK_TO_CRYPTO_RIJNDAEL
+else ifeq ($(DP_LINK_CRYPTO_RIJNDAEL), dlopen)
        LIB_CRYPTO_RIJNDAEL=
        CFLAGS_CRYPTO_RIJNDAEL=
 endif
@@ -311,8 +319,11 @@ ifeq ($(DP_LINK_XMP), shared)
        OBJ_SND_XMP=snd_xmp.o
        LIB_SND_XMP=-lxmp
        CFLAGS_SND_XMP=-DUSEXMP -DLINK_TO_LIBXMP
-endif
-ifeq ($(DP_LINK_XMP), dlopen)
+else ifeq ($(DP_LINK_XMP), static)
+       OBJ_SND_XMP=snd_xmp.o
+       LIB_SND_XMP=-l:libxmp.a
+       CFLAGS_SND_XMP=-DUSEXMP -DLINK_TO_LIBXMP
+else ifeq ($(DP_LINK_XMP), dlopen)
        OBJ_SND_XMP=snd_xmp.o
        LIB_SND_XMP=
        CFLAGS_SND_XMP=-DUSEXMP
@@ -320,10 +331,6 @@ endif
 
 
 ##### Extra CFLAGS #####
-ifneq ($(CC), tcc)
-       CFLAGS_MAKEDEP?=-MMD
-endif
-
 ifdef DP_FS_BASEDIR
        CFLAGS_FS=-DDP_FS_BASEDIR=\"$(DP_FS_BASEDIR)\"
 else
@@ -346,6 +353,7 @@ CFLAGS_NET=
 # Systems without IPv6 support should uncomment this:
 #CFLAGS_NET+=-DNOSUPPORTIPV6
 
+
 ##### GNU Make specific definitions #####
 
 DO_LD=$(CC) -o ../../../$@ $^ $(LDFLAGS)
index 8c4b4d582b31ebe5f7bb4b200ac38c59c5633724..dc9c990e31dab1754b8e40414b320db135f98ed4 100644 (file)
@@ -2,44 +2,7 @@
 CHECKLEVEL1 = @if [ "$(LEVEL)" != 1 ]; then $(MAKE) help; false; fi
 CHECKLEVEL2 = @if [ "$(LEVEL)" != 2 ]; then $(MAKE) help; false; fi
 
-# Choose the compiler you want to use
-CC?=gcc
-
-# athlon optimizations
-#CPUOPTIMIZATIONS?=-march=athlon
-# athlon xp optimizations
-#CPUOPTIMIZATIONS?=-march=athlon-xp
-# athlon 64 optimizations
-#CPUOPTIMIZATIONS?=-march=athlon64 -m32
-# Pentium 3 optimizations
-#CPUOPTIMIZATIONS?=-march=pentium3
-# Pentium 4 optimizations
-#CPUOPTIMIZATIONS?=-march=pentium4
-# 686 (Pentium Pro/II) optimizations
-#CPUOPTIMIZATIONS?=-march=i686
-# No specific CPU (386 compatible)
-#CPUOPTIMIZATIONS?=
-# Experimental
-#CPUOPTIMIZATIONS?=-fno-math-errno -fno-rounding-math -fno-signaling-nans -fassociative-math -freciprocal-math -fno-signed-zeros -fno-trapping-math
-# Normal
-ifeq ($(CC), clang)
-       CPUOPTIMIZATIONS?=-fno-math-errno -fno-rounding-math -fno-trapping-math
-else
-       CPUOPTIMIZATIONS?=-fno-math-errno -fno-rounding-math -fno-signaling-nans -fno-trapping-math
-endif
-# NOTE: *never* *ever* use the -ffast-math or -funsafe-math-optimizations flag
-# Also, since gcc 5, -ffinite-math-only makes NaN and zero compare equal inside engine code but not inside QC, which causes error spam for seemingly valid QC code like if (x != 0) return 1 / x;
 
-SDL_CONFIG?=sdl2-config
-SDLCONFIG_UNIXCFLAGS?=`$(SDL_CONFIG) --cflags`
-SDLCONFIG_UNIXCFLAGS_X11?=
-SDLCONFIG_UNIXLIBS?=`$(SDL_CONFIG) --libs`
-SDLCONFIG_UNIXLIBS_X11?=-lX11
-SDLCONFIG_UNIXSTATICLIBS?=`$(SDL_CONFIG) --static-libs`
-SDLCONFIG_UNIXSTATICLIBS_X11?=-lX11
-SDLCONFIG_MACOSXCFLAGS=-I/Library/Frameworks/SDL2.framework/Headers -I$(HOME)/Library/Frameworks/SDL2.framework/Headers
-SDLCONFIG_MACOSXLIBS=-F$(HOME)/Library/Frameworks/ -framework SDL2 -framework Cocoa $(SDLCONFIG_MACOSXCFLAGS)
-SDLCONFIG_MACOSXSTATICLIBS=-F$(HOME)/Library/Frameworks/ -framework SDL2 -framework Cocoa $(SDLCONFIG_MACOSXCFLAGS)
 STRIP?=strip
 
 
@@ -118,6 +81,7 @@ OBJ_COMMON= \
        model_sprite.o \
        netconn.o \
        palette.o \
+       phys.o \
        polygon.o \
        portals.o \
        protocol.o \
@@ -162,21 +126,26 @@ OBJ_MENU= \
 # note that builddate.c is very intentionally not compiled to a .o before
 # being linked, because it should be recompiled every time an executable is
 # built to give the executable a proper date string
-OBJ_SV= builddate.c sys_unix.o vid_null.o thread_null.o $(OBJ_SND_NULL) $(OBJ_COMMON)
+OBJ_SV= builddate.c sys_null.o vid_null.o thread_null.o $(OBJ_SND_NULL) $(OBJ_COMMON)
 OBJ_SDL= builddate.c sys_sdl.o vid_sdl.o thread_sdl.o $(OBJ_MENU) $(OBJ_SND_COMMON) $(OBJ_SND_XMP) snd_sdl.o $(OBJ_VIDEO_CAPTURE) $(OBJ_COMMON)
 
 
 # Compilation
+# -D_POSIX_C_SOURCE=200809L doesn't enable all of POSIX 2008, wtf?
+# -D_DEFAULT_SOURCE does enables all of POSIX 2008 (without GNU extensions).
 ifeq ($(PEDANTIC),1)
-       CFLAGS_STANDARD=-std=c11 -pedantic -D_POSIX_C_SOURCE=200809L -DCONFIG_PEDANTIC
+       CFLAGS_STANDARD=-std=c17 -pedantic -D_DEFAULT_SOURCE -DCONFIG_PEDANTIC
 else
        CFLAGS_STANDARD=
 endif
 
-CFLAGS_TCC=
+CFLAGS_WARNINGS=-Wall -Werror=vla -Werror=c++-compat -Wwrite-strings -Wshadow -Wold-style-definition -Wstrict-prototypes -Wsign-compare -Wdeclaration-after-statement -Wmissing-prototypes
 
+CFLAGS_TCC=
 ifeq ($(CC), tcc)
        CFLAGS_TCC=-DSDL_DISABLE_IMMINTRIN_H
+else
+       CFLAGS_MAKEDEP?=-MMD
 endif
 
 CFLAGS_COMMON=$(CFLAGS_STANDARD) $(CFLAGS_TCC) $(CFLAGS_MAKEDEP) $(CFLAGS_PRELOAD) $(CFLAGS_FS) $(CFLAGS_WARNINGS) $(CFLAGS_LIBZ) $(CFLAGS_LIBJPEG) $(CFLAGS_SND_XMP) $(CFLAGS_NET) $(CFLAGS_SDL) -D_FILE_OFFSET_BITS=64 -D__KERNEL_STRICT_NAMES -I../../../
@@ -196,12 +165,25 @@ else
        CFLAGS_SSE2=
 endif # ifeq ($(DP_SSE),1)
 
-OPTIM_DEBUG=$(CPUOPTIMIZATIONS)
+# No specific CPU
+CPUOPTIMIZATIONS?=
+# x86
+ifeq ($(DP_SSE),1)
+       CPUOPTIMIZATIONS+=-mno-avx
+endif
+# bones_was_here: added -mno-avx because when compiling for (at least) haswell or skylake with gcc or clang, with both -O2 and -O3, AVX auto-vectorisation causes subtle bugs in Xonotic QC physics, and changes the hash generated by the CI pipeline.  AVX2 seems to be OK.
+
+# We can get good stack traces at -O1 with -fno-omit-frame-pointer (as recommended by ASan docs) for a hefty boost compared to unoptimised.
+OPTIM_DEBUG=-O1 -fno-omit-frame-pointer $(CPUOPTIMIZATIONS)
 #OPTIM_RELEASE=-O2 -fno-strict-aliasing -ffast-math -funroll-loops $(CPUOPTIMIZATIONS)
 #OPTIM_RELEASE=-O2 -fno-strict-aliasing -fno-math-errno -fno-trapping-math -fno-signaling-nans -fcx-limited-range -funroll-loops $(CPUOPTIMIZATIONS)
 #OPTIM_RELEASE=-O2 -fno-strict-aliasing -funroll-loops $(CPUOPTIMIZATIONS)
 #OPTIM_RELEASE=-O2 -fno-strict-aliasing $(CPUOPTIMIZATIONS)
-OPTIM_RELEASE=-O3 -fno-strict-aliasing $(CPUOPTIMIZATIONS)
+#OPTIM_RELEASE=-O3 -fno-strict-aliasing $(CPUOPTIMIZATIONS)
+#OPTIM_RELEASE=-O3 -fno-strict-aliasing -fno-math-errno -fno-trapping-math $(CPUOPTIMIZATIONS)
+# -Winline is here instead of in CFLAGS_WARNINGS because we need inlining disabled for good stack traces in debug builds,
+# also because UBSan and ASan cause this warning to be spammed.
+OPTIM_RELEASE=-O3 -fno-math-errno -fno-trapping-math $(CPUOPTIMIZATIONS) -Winline
 # NOTE: *never* *ever* use the -ffast-math or -funsafe-math-optimizations flag
 # Also, since gcc 5, -ffinite-math-only makes NaN and zero compare equal inside engine code but not inside QC, which causes error spam for seemingly valid QC code like if (x != 0) return 1 / x;
 
@@ -209,9 +191,13 @@ DO_CC=$(CC) $(CFLAGS) -c $< -o $@
 
 
 # Link
-LDFLAGS_DEBUG=-g -ggdb $(OPTIM_DEBUG) -DSVNREVISION=`{ test -d .svn && svnversion; } || { test -d .git && git describe --always; } || echo -` -DBUILDTYPE=debug
-LDFLAGS_PROFILE=-g -pg -fprofile-arcs $(OPTIM_RELEASE) -DSVNREVISION=`{ test -d .svn && svnversion; } || { test -d .git && git describe --always; } || echo -` -DBUILDTYPE=profile
-LDFLAGS_RELEASE=$(OPTIM_RELEASE) -DSVNREVISION=`{ test -d .svn && svnversion; } || { test -d .git && git describe --always; } || echo -` -DBUILDTYPE=release
+
+# not checking for .git directory before running `git describe` because that would stop it working in a subdirectory
+VCREVISION=$(shell { test -d .svn && svnversion; } || git describe --always --dirty='~' 2>/dev/null || echo -)
+
+LDFLAGS_DEBUG=-g -ggdb $(OPTIM_DEBUG)                  -DVCREVISION=$(VCREVISION) -DBUILDTYPE=debug
+LDFLAGS_PROFILE=-g -pg -fprofile-arcs $(OPTIM_RELEASE) -DVCREVISION=$(VCREVISION) -DBUILDTYPE=profile
+LDFLAGS_RELEASE=$(OPTIM_RELEASE)                       -DVCREVISION=$(VCREVISION) -DBUILDTYPE=release
 
 
 ##### UNIX specific variables #####
@@ -235,15 +221,17 @@ CMD_UNIXMKDIR=mkdir -p
 ##### Linux specific variables #####
 
 # Link
-LDFLAGS_LINUXSV=$(LDFLAGS_UNIXCOMMON) -lrt -ldl
-LDFLAGS_LINUXSDL=$(LDFLAGS_UNIXCOMMON) -lrt -ldl $(LDFLAGS_UNIXSDL)
+# -rdynamic allows glibc backtrace_symbols_fd() to convert addresses to function names
+# with a much smaller binary than with full debug symbols
+LDFLAGS_LINUXSV=$(LDFLAGS_UNIXCOMMON) -lrt -ldl -rdynamic
+LDFLAGS_LINUXSDL=$(LDFLAGS_UNIXCOMMON) -lrt -ldl -rdynamic $(LDFLAGS_UNIXSDL)
 
 
 ##### Mac OS X specific variables #####
 
 # Link
 LDFLAGS_MACOSXSV=$(LDFLAGS_UNIXCOMMON) -ldl
-LDFLAGS_MACOSXSDL=$(LDFLAGS_UNIXCOMMON) -ldl -framework IOKit $(SDLCONFIG_STATICLIBS) ../../../SDLMain.m
+LDFLAGS_MACOSXSDL=$(LDFLAGS_UNIXCOMMON) -ldl -framework IOKit $(SDLCONFIG_STATICLIBS) #../../../SDLMain.m
 
 
 ##### SunOS specific variables #####
@@ -485,6 +473,7 @@ nexuiz.o: %.o : %.rc
 .c.o:
        $(CHECKLEVEL2)
        $(DO_CC)
+.c.o: .EXTRA_PREREQS =  # According to POSIX, a suffix rule cannot contain prerequisites.
 
 $(EXE_SV): $(OBJ_SV) $(OBJ_ICON)
        $(CHECKLEVEL2)
@@ -502,6 +491,9 @@ $(EXE_SDLNEXUIZ): $(OBJ_SDL) $(OBJ_ICON_NEXUIZ)
        $(CHECKLEVEL2)
        $(DO_LD)
 
+# If requested, these targets must always run first:
+.EXTRA_PREREQS := $(filter clean clean-profile, $(MAKECMDGOALS))
+
 clean:
        -$(CMD_RM) $(EXE_SV)
        -$(CMD_RM) $(EXE_SDL)
@@ -511,6 +503,7 @@ clean:
        -$(CMD_RM) *.d
        -$(CMD_RM) *.gch
        -$(CMD_RM) build-obj/
+clean: .EXTRA_PREREQS =  # prevents circular dependency with clean-profile
 
 clean-profile: clean
        -$(CMD_RM) *.gcda
index 55f1218e462d0571a44fd3313ed51292d9f6697b..2fa93a1cb6626b7826922aeae0e0c314d82cdbc6 100644 (file)
--- a/mathlib.h
+++ b/mathlib.h
@@ -31,23 +31,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 struct mplane_s;
 extern vec3_t vec3_origin;
 
-#define float_nanmask (0x7F800000)
-#define double_nanmask (0x7FF8000000000000)
-#define FLOAT_IS_NAN(x) (((*(int *)&x)&float_nanmask)==float_nanmask)
-#define DOUBLE_IS_NAN(x) (((*(long long *)&x)&double_nanmask)==double_nanmask)
-
-#ifdef VEC_64
-#define VEC_IS_NAN(x) DOUBLE_IS_NAN(x)
-#else
-#define VEC_IS_NAN(x) FLOAT_IS_NAN(x)
-#endif
-
-#ifdef PRVM_64
-#define PRVM_IS_NAN(x) DOUBLE_IS_NAN(x)
-#else
-#define PRVM_IS_NAN(x) FLOAT_IS_NAN(x)
-#endif
-
 #define bound(min,num,max) ((num) >= (min) ? ((num) < (max) ? (num) : (max)) : (min))
 
 #ifndef min
@@ -92,7 +75,7 @@ unsigned int CeilPowerOf2(unsigned int value);
 #define Vector2Negate(a,b) ((b)[0]=-((a)[0]),(b)[1]=-((a)[1]))
 #define Vector2Set(a,b,c) ((a)[0]=(b),(a)[1]=(c))
 #define Vector2Scale(in, scale, out) ((out)[0] = (in)[0] * (scale),(out)[1] = (in)[1] * (scale))
-#define Vector2Normalize2(v,dest) {float ilength = (float) sqrt(DotProduct2((v),(v)));if (ilength) ilength = 1.0f / ilength;dest[0] = (v)[0] * ilength;dest[1] = (v)[1] * ilength;}
+#define Vector2Normalize2(v,dest) {float ilength = (float)DotProduct2((v),(v));if (ilength) ilength = 1.0f / sqrt(ilength);dest[0] = (v)[0] * ilength;dest[1] = (v)[1] * ilength;}
 
 #define DotProduct4(a,b) ((a)[0]*(b)[0]+(a)[1]*(b)[1]+(a)[2]*(b)[2]+(a)[3]*(b)[3])
 #define Vector4Clear(a) ((a)[0]=(a)[1]=(a)[2]=(a)[3]=0)
@@ -100,7 +83,7 @@ unsigned int CeilPowerOf2(unsigned int value);
 #define Vector4Copy(a,b) ((b)[0]=(a)[0],(b)[1]=(a)[1],(b)[2]=(a)[2],(b)[3]=(a)[3])
 #define Vector4Negate(a,b) ((b)[0]=-((a)[0]),(b)[1]=-((a)[1]),(b)[2]=-((a)[2]),(b)[3]=-((a)[3]))
 #define Vector4Set(a,b,c,d,e) ((a)[0]=(b),(a)[1]=(c),(a)[2]=(d),(a)[3]=(e))
-#define Vector4Normalize2(v,dest) {float ilength = (float) sqrt(DotProduct4((v),(v)));if (ilength) ilength = 1.0f / ilength;dest[0] = (v)[0] * ilength;dest[1] = (v)[1] * ilength;dest[2] = (v)[2] * ilength;dest[3] = (v)[3] * ilength;}
+#define Vector4Normalize2(v,dest) {float ilength = (float)DotProduct4((v),(v));if (ilength) ilength = 1.0f / sqrt(ilength);dest[0] = (v)[0] * ilength;dest[1] = (v)[1] * ilength;dest[2] = (v)[2] * ilength;dest[3] = (v)[3] * ilength;}
 #define Vector4Subtract(a,b,c) ((c)[0]=(a)[0]-(b)[0],(c)[1]=(a)[1]-(b)[1],(c)[2]=(a)[2]-(b)[2],(c)[3]=(a)[3]-(b)[3])
 #define Vector4Add(a,b,c) ((c)[0]=(a)[0]+(b)[0],(c)[1]=(a)[1]+(b)[1],(c)[2]=(a)[2]+(b)[2],(c)[3]=(a)[3]+(b)[3])
 #define Vector4Scale(in, scale, out) ((out)[0] = (in)[0] * (scale),(out)[1] = (in)[1] * (scale),(out)[2] = (in)[2] * (scale),(out)[3] = (in)[3] * (scale))
@@ -117,9 +100,9 @@ unsigned int CeilPowerOf2(unsigned int value);
 #define VectorCopy(a,b) ((b)[0]=(a)[0],(b)[1]=(a)[1],(b)[2]=(a)[2])
 #define VectorMultiply(a,b,c) ((c)[0]=(a)[0]*(b)[0],(c)[1]=(a)[1]*(b)[1],(c)[2]=(a)[2]*(b)[2])
 #define CrossProduct(a,b,c) ((c)[0]=(a)[1]*(b)[2]-(a)[2]*(b)[1],(c)[1]=(a)[2]*(b)[0]-(a)[0]*(b)[2],(c)[2]=(a)[0]*(b)[1]-(a)[1]*(b)[0])
-#define VectorNormalize(v) {float ilength = (float) sqrt(DotProduct((v),(v)));if (ilength) ilength = 1.0f / ilength;(v)[0] *= ilength;(v)[1] *= ilength;(v)[2] *= ilength;}
-#define VectorNormalize2(v,dest) {float ilength = (float) sqrt(DotProduct((v),(v)));if (ilength) ilength = 1.0f / ilength;dest[0] = (v)[0] * ilength;dest[1] = (v)[1] * ilength;dest[2] = (v)[2] * ilength;}
-#define VectorNormalizeDouble(v) {double ilength = sqrt(DotProduct((v),(v)));if (ilength) ilength = 1.0 / ilength;(v)[0] *= ilength;(v)[1] *= ilength;(v)[2] *= ilength;}
+#define VectorNormalize(v) {float ilength = (float)DotProduct((v),(v));if (ilength) ilength = 1.0f / sqrt(ilength);(v)[0] *= ilength;(v)[1] *= ilength;(v)[2] *= ilength;}
+#define VectorNormalize2(v,dest) {float ilength = (float)DotProduct((v),(v));if (ilength) ilength = 1.0f / sqrt(ilength);dest[0] = (v)[0] * ilength;dest[1] = (v)[1] * ilength;dest[2] = (v)[2] * ilength;}
+#define VectorNormalizeDouble(v) {double ilength = DotProduct((v),(v));if (ilength) ilength = 1.0 / sqrt(ilength);(v)[0] *= ilength;(v)[1] *= ilength;(v)[2] *= ilength;}
 #define VectorDistance2(a, b) (((a)[0] - (b)[0]) * ((a)[0] - (b)[0]) + ((a)[1] - (b)[1]) * ((a)[1] - (b)[1]) + ((a)[2] - (b)[2]) * ((a)[2] - (b)[2]))
 #define VectorDistance(a, b) (sqrt(VectorDistance2(a,b)))
 #define VectorLength(a) (sqrt((double)DotProduct(a, a)))
@@ -235,14 +218,9 @@ float r2is = 1.0f / sin(r2);\
 
 #define VectorCopy4(a,b) {(b)[0]=(a)[0];(b)[1]=(a)[1];(b)[2]=(a)[2];(b)[3]=(a)[3];}
 
-vec_t Length (vec3_t v);
-
 /// returns vector length
 float VectorNormalizeLength (vec3_t v);
 
-/// returns vector length
-float VectorNormalizeLength2 (vec3_t v, vec3_t dest);
-
 #define NUMVERTEXNORMALS       162
 extern float m_bytenormals[NUMVERTEXNORMALS][3];
 
index dd6f3c6a348919f14b7620cf2a0ac1daea2e352e..ed9d141341ce7b181dd6a42a49c4d518cff40988 100644 (file)
@@ -1123,42 +1123,42 @@ void Matrix4x4_ToArrayDoubleD3D(const matrix4x4_t *in, double out[16])
 #endif
 }
 
-void Matrix4x4_FromArrayDoubleD3D (matrix4x4_t *out, const double in[16])
+void Matrix4x4_FromArrayDoubleD3D (matrix4x4_t *out, const double in[4][4])
 {
 #ifdef MATRIX4x4_OPENGLORIENTATION
-       out->m[0][0] = in[0];
-       out->m[1][0] = in[1];
-       out->m[2][0] = in[2];
-       out->m[3][0] = in[3];
-       out->m[0][1] = in[4];
-       out->m[1][1] = in[5];
-       out->m[2][1] = in[6];
-       out->m[3][1] = in[7];
-       out->m[0][2] = in[8];
-       out->m[1][2] = in[9];
-       out->m[2][2] = in[10];
-       out->m[3][2] = in[11];
-       out->m[0][3] = in[12];
-       out->m[1][3] = in[13];
-       out->m[2][3] = in[14];
-       out->m[3][3] = in[15];
+       out->m[0][0] = in[0][0];
+       out->m[1][0] = in[0][1];
+       out->m[2][0] = in[0][2];
+       out->m[3][0] = in[0][3];
+       out->m[0][1] = in[1][0];
+       out->m[1][1] = in[1][1];
+       out->m[2][1] = in[1][2];
+       out->m[3][1] = in[1][3];
+       out->m[0][2] = in[2][0];
+       out->m[1][2] = in[2][1];
+       out->m[2][2] = in[2][2];
+       out->m[3][2] = in[2][3];
+       out->m[0][3] = in[3][0];
+       out->m[1][3] = in[3][1];
+       out->m[2][3] = in[3][2];
+       out->m[3][3] = in[3][3];
 #else
-       out->m[0][0] = in[0];
-       out->m[0][1] = in[1];
-       out->m[0][2] = in[2];
-       out->m[0][3] = in[3];
-       out->m[1][0] = in[4];
-       out->m[1][1] = in[5];
-       out->m[1][2] = in[6];
-       out->m[1][3] = in[7];
-       out->m[2][0] = in[8];
-       out->m[2][1] = in[9];
-       out->m[2][2] = in[10];
-       out->m[2][3] = in[11];
-       out->m[3][0] = in[12];
-       out->m[3][1] = in[13];
-       out->m[3][2] = in[14];
-       out->m[3][3] = in[15];
+       out->m[0][0] = in[0][0];
+       out->m[0][1] = in[0][1];
+       out->m[0][2] = in[0][2];
+       out->m[0][3] = in[0][3];
+       out->m[1][0] = in[1][0];
+       out->m[1][1] = in[1][1];
+       out->m[1][2] = in[1][2];
+       out->m[1][3] = in[1][3];
+       out->m[2][0] = in[2][0];
+       out->m[2][1] = in[2][1];
+       out->m[2][2] = in[2][2];
+       out->m[2][3] = in[2][3];
+       out->m[3][0] = in[3][0];
+       out->m[3][1] = in[3][1];
+       out->m[3][2] = in[3][2];
+       out->m[3][3] = in[3][3];
 #endif
 }
 
@@ -1318,34 +1318,34 @@ void Matrix4x4_FromArrayFloatD3D (matrix4x4_t *out, const float in[16])
 #endif
 }
 
-void Matrix4x4_ToArray12FloatGL(const matrix4x4_t *in, float out[12])
+void Matrix4x4_ToArray12FloatGL(const matrix4x4_t *in, float out[4][3])
 {
 #ifdef MATRIX4x4_OPENGLORIENTATION
-       out[ 0] = in->m[0][0];
-       out[ 1] = in->m[0][1];
-       out[ 2] = in->m[0][2];
-       out[ 3] = in->m[1][0];
-       out[ 4] = in->m[1][1];
-       out[ 5] = in->m[1][2];
-       out[ 6] = in->m[2][0];
-       out[ 7] = in->m[2][1];
-       out[ 8] = in->m[2][2];
-       out[ 9] = in->m[3][0];
-       out[10] = in->m[3][1];
-       out[11] = in->m[3][2];
+       out[0][0] = in->m[0][0];
+       out[0][1] = in->m[0][1];
+       out[0][2] = in->m[0][2];
+       out[1][0] = in->m[1][0];
+       out[1][1] = in->m[1][1];
+       out[1][2] = in->m[1][2];
+       out[2][0] = in->m[2][0];
+       out[2][1] = in->m[2][1];
+       out[2][2] = in->m[2][2];
+       out[3][0] = in->m[3][0];
+       out[3][1] = in->m[3][1];
+       out[3][2] = in->m[3][2];
 #else
-       out[ 0] = in->m[0][0];
-       out[ 1] = in->m[1][0];
-       out[ 2] = in->m[2][0];
-       out[ 3] = in->m[0][1];
-       out[ 4] = in->m[1][1];
-       out[ 5] = in->m[2][1];
-       out[ 6] = in->m[0][2];
-       out[ 7] = in->m[1][2];
-       out[ 8] = in->m[2][2];
-       out[ 9] = in->m[0][3];
-       out[10] = in->m[1][3];
-       out[11] = in->m[2][3];
+       out[0][0] = in->m[0][0];
+       out[0][1] = in->m[1][0];
+       out[0][2] = in->m[2][0];
+       out[1][0] = in->m[0][1];
+       out[1][1] = in->m[1][1];
+       out[1][2] = in->m[2][1];
+       out[2][0] = in->m[0][2];
+       out[2][1] = in->m[1][2];
+       out[2][2] = in->m[2][2];
+       out[3][0] = in->m[0][3];
+       out[3][1] = in->m[1][3];
+       out[3][2] = in->m[2][3];
 #endif
 }
 
index cf76252447e6104d3100f32fb611692558aea90a..bfae8c47a3b335605bd694e105a695abbc51c2c5 100644 (file)
@@ -93,7 +93,7 @@ void Matrix4x4_FromArrayDoubleGL(matrix4x4_t *out, const double in[16]);
 // converts a matrix4x4 to a double[16] array in the Direct3D orientation
 void Matrix4x4_ToArrayDoubleD3D(const matrix4x4_t *in, double out[16]);
 // creates a matrix4x4 from a double[16] array in the Direct3D orientation
-void Matrix4x4_FromArrayDoubleD3D(matrix4x4_t *out, const double in[16]);
+void Matrix4x4_FromArrayDoubleD3D(matrix4x4_t *out, const double in[4][4]);
 
 // converts a matrix4x4 to a float[16] array in the OpenGL orientation
 void Matrix4x4_ToArrayFloatGL(const matrix4x4_t *in, float out[16]);
@@ -105,7 +105,7 @@ void Matrix4x4_ToArrayFloatD3D(const matrix4x4_t *in, float out[16]);
 void Matrix4x4_FromArrayFloatD3D(matrix4x4_t *out, const float in[16]);
 
 // converts a matrix4x4 to a float[12] array in the OpenGL orientation
-void Matrix4x4_ToArray12FloatGL(const matrix4x4_t *in, float out[12]);
+void Matrix4x4_ToArray12FloatGL(const matrix4x4_t *in, float out[4][3]);
 // creates a matrix4x4 from a float[12] array in the OpenGL orientation
 void Matrix4x4_FromArray12FloatGL(matrix4x4_t *out, const float in[12]);
 // converts a matrix4x4 to a float[12] array in the Direct3D orientation
index ae92e3758af2ac487f1b732662fe731a6d1cda79..5164994a01e246974c6ae8995a072c1f4b39f1b6 100644 (file)
--- a/mdfour.c
+++ b/mdfour.c
@@ -98,8 +98,9 @@ static void copy64(uint32_t *M, const unsigned char *in)
 {
        int i;
 
+       // UBSan: (unsigned) is because promotion to int causes signed overflow when in[] >= 128
        for (i=0;i<16;i++)
-               M[i] = (in[i*4+3]<<24) | (in[i*4+2]<<16) |
+               M[i] = ((unsigned)in[i*4+3]<<24) | (in[i*4+2]<<16) |
                        (in[i*4+1]<<8) | (in[i*4+0]<<0);
 }
 
diff --git a/menu.c b/menu.c
index ce154097118e91e32c8aef969f53f0c789caa89a..7b51ab4cf523f2069b30bfc1734903b04975ff7a 100644 (file)
--- a/menu.c
+++ b/menu.c
@@ -34,7 +34,6 @@ static cvar_t menu_progs = {CF_CLIENT, "menu_progs", "menu.dat", "name of quakec
 static int NehGameType;
 
 enum m_state_e m_state;
-char m_return_reason[128];
 
 void M_Menu_Main_f(cmd_state_t *cmd);
        void M_Menu_SinglePlayer_f(cmd_state_t *cmd);
@@ -108,13 +107,6 @@ static void M_ModList_Key(cmd_state_t *cmd, int key, int ascii);
 
 static qbool   m_entersound;           ///< play after drawing a frame, so caching won't disrupt the sound
 
-void M_Update_Return_Reason(const char *s)
-{
-       strlcpy(m_return_reason, s, sizeof(m_return_reason));
-       if (s)
-               Con_DPrintf("%s\n", s);
-}
-
 #define StartingGame   (m_multiplayer_cursor == 1)
 #define JoiningGame            (m_multiplayer_cursor == 0)
 
@@ -858,7 +850,7 @@ static void M_ScanSaves (void)
 
        for (i=0 ; i<MAX_SAVEGAMES ; i++)
        {
-               strlcpy (m_filenames[i], "--- UNUSED SLOT ---", sizeof(m_filenames[i]));
+               dp_strlcpy (m_filenames[i], "--- UNUSED SLOT ---", sizeof(m_filenames[i]));
                loadable[i] = false;
                dpsnprintf (name, sizeof(name), "s%i.sav", (int)i);
                f = FS_OpenRealFile (name, "rb", false);
@@ -874,7 +866,7 @@ static void M_ScanSaves (void)
                //version = atoi(com_token);
                // description
                COM_ParseToken_Simple(&t, false, false, true);
-               strlcpy (m_filenames[i], com_token, sizeof (m_filenames[i]));
+               dp_strlcpy (m_filenames[i], com_token, sizeof (m_filenames[i]));
 
        // change _ back to space
                for (j=0 ; j<SAVEGAME_COMMENT_LENGTH ; j++)
@@ -1292,7 +1284,7 @@ void M_Menu_Setup_f(cmd_state_t *cmd)
        key_dest = key_menu;
        m_state = m_setup;
        m_entersound = true;
-       strlcpy(setup_myname, cl_name.string, sizeof(setup_myname));
+       dp_strlcpy(setup_myname, cl_name.string, sizeof(setup_myname));
        setup_top = setup_oldtop = cl_topcolor.integer;
        setup_bottom = setup_oldbottom = cl_bottomcolor.integer;
        setup_rate = cl_rate.integer;
@@ -2640,18 +2632,18 @@ static void M_Keys_Draw (void)
 
                // LadyHavoc: redesigned to print more than 2 keys, inspired by Tomaz's MiniRacer
                if (keys[0] == -1)
-                       strlcpy(keystring, "???", sizeof(keystring));
+                       dp_strlcpy(keystring, "???", sizeof(keystring));
                else
                {
-                       char tinystr[2];
+                       char tinystr[TINYSTR_LEN];
                        keystring[0] = 0;
                        for (j = 0;j < NUMKEYS;j++)
                        {
                                if (keys[j] != -1)
                                {
                                        if (j > 0)
-                                               strlcat(keystring, " or ", sizeof(keystring));
-                                       strlcat(keystring, Key_KeynumToString (keys[j], tinystr, sizeof(tinystr)), sizeof(keystring));
+                                               dp_strlcat(keystring, " or ", sizeof(keystring));
+                                       dp_strlcat(keystring, Key_KeynumToString (keys[j], tinystr, TINYSTR_LEN), sizeof(keystring));
                                }
                        }
                }
@@ -2669,7 +2661,7 @@ static void M_Keys_Key(cmd_state_t *cmd, int k, int ascii)
 {
        char    line[80];
        int             keys[NUMKEYS];
-       char    tinystr[2];
+       char    tinystr[TINYSTR_LEN];
 
        if (bind_grab)
        {       // defining a key
@@ -2680,7 +2672,7 @@ static void M_Keys_Key(cmd_state_t *cmd, int k, int ascii)
                }
                else //if (k != '`')
                {
-                       dpsnprintf(line, sizeof(line), "bind \"%s\" \"%s\"\n", Key_KeynumToString(k, tinystr, sizeof(tinystr)), bindnames[keys_cursor][0]);
+                       dpsnprintf(line, sizeof(line), "bind \"%s\" \"%s\"\n", Key_KeynumToString(k, tinystr, TINYSTR_LEN), bindnames[keys_cursor][0]);
                        Cbuf_InsertText (cmd, line);
                }
 
@@ -3344,7 +3336,7 @@ void M_Menu_LanConfig_f(cmd_state_t *cmd)
        lanConfig_port = 26000;
        dpsnprintf(lanConfig_portname, sizeof(lanConfig_portname), "%u", (unsigned int) lanConfig_port);
 
-       M_Update_Return_Reason("");
+       cl_connect_status[0] = '\0';
 }
 
 
@@ -3397,8 +3389,8 @@ static void M_LanConfig_Draw (void)
        if (lanConfig_cursor == 3)
                M_DrawCharacter (basex+16 + 8*strlen(lanConfig_joinname), lanConfig_cursor_table [lanConfig_cursor], 10+((int)(host.realtime*4)&1));
 
-       if (*m_return_reason)
-               M_Print(basex, 168, m_return_reason);
+       if (*cl_connect_status)
+               M_Print(basex, 168, cl_connect_status);
 }
 
 
@@ -4385,7 +4377,8 @@ static void M_GameOptions_Key(cmd_state_t *cmd, int key, int ascii)
 //=============================================================================
 /* SLIST MENU */
 
-static int slist_cursor;
+static unsigned slist_cursor;
+static unsigned slist_visible;
 
 void M_Menu_ServerList_f(cmd_state_t *cmd)
 {
@@ -4393,7 +4386,7 @@ void M_Menu_ServerList_f(cmd_state_t *cmd)
        m_state = m_slist;
        m_entersound = true;
        slist_cursor = 0;
-       M_Update_Return_Reason("");
+       cl_connect_status[0] = '\0';
        if (lanConfig_cursor == 2)
                Net_SlistQW_f(cmd);
        else
@@ -4403,7 +4396,7 @@ void M_Menu_ServerList_f(cmd_state_t *cmd)
 
 static void M_ServerList_Draw (void)
 {
-       int n, y, visible, start, end, statnumplayers, statmaxplayers;
+       unsigned n, y, start, end, statnumplayers, statmaxplayers;
        cachepic_t *p;
        const char *s;
        char vabuf[1024];
@@ -4415,14 +4408,14 @@ static void M_ServerList_Draw (void)
                M_Background(640, vid_conheight.integer);
        // scroll the list as the cursor moves
        ServerList_GetPlayerStatistics(&statnumplayers, &statmaxplayers);
-       s = va(vabuf, sizeof(vabuf), "%i/%i masters %i/%i servers %i/%i players", masterreplycount, masterquerycount, serverreplycount, serverquerycount, statnumplayers, statmaxplayers);
+       s = va(vabuf, sizeof(vabuf), "%u/%u masters %u/%u servers %u/%u players", masterreplycount, masterquerycount, serverreplycount, serverquerycount, statnumplayers, statmaxplayers);
        M_PrintRed((640 - strlen(s) * 8) / 2, 32, s);
-       if (*m_return_reason)
-               M_Print(16, menu_height - 8, m_return_reason);
+       if (*cl_connect_status)
+               M_Print(16, menu_height - 8, cl_connect_status);
        y = 48;
-       visible = (int)((menu_height - 16 - y) / 8 / 2);
-       start = bound(0, slist_cursor - (visible >> 1), serverlist_viewcount - visible);
-       end = min(start + visible, serverlist_viewcount);
+       slist_visible = (menu_height - 16 - y) / 8 / 2;
+       start = min(slist_cursor - min(slist_cursor, slist_visible >> 1), serverlist_viewcount - min(serverlist_viewcount, slist_visible));
+       end = min(start + slist_visible, serverlist_viewcount);
 
        p = Draw_CachePic ("gfx/p_multi");
        M_DrawPic((640 - Draw_GetPicWidth(p)) / 2, 4, "gfx/p_multi");
@@ -4469,14 +4462,18 @@ static void M_ServerList_Key(cmd_state_t *cmd, int k, int ascii)
                        Net_Slist_f(cmd);
                break;
 
+       case K_PGUP:
+               slist_cursor -= slist_visible - 2;
        case K_UPARROW:
        case K_LEFTARROW:
                S_LocalSound ("sound/misc/menu1.wav");
                slist_cursor--;
-               if (slist_cursor < 0)
+               if (slist_cursor >= serverlist_viewcount)
                        slist_cursor = serverlist_viewcount - 1;
                break;
 
+       case K_PGDN:
+               slist_cursor += slist_visible - 2;
        case K_DOWNARROW:
        case K_RIGHTARROW:
                S_LocalSound ("sound/misc/menu1.wav");
@@ -4485,6 +4482,16 @@ static void M_ServerList_Key(cmd_state_t *cmd, int k, int ascii)
                        slist_cursor = 0;
                break;
 
+       case K_HOME:
+               S_LocalSound ("sound/misc/menu1.wav");
+               slist_cursor = 0;
+               break;
+
+       case K_END:
+               S_LocalSound ("sound/misc/menu1.wav");
+               slist_cursor = serverlist_viewcount - 1;
+               break;
+
        case K_ENTER:
                S_LocalSound ("sound/misc/menu2.wav");
                if (serverlist_viewcount)
@@ -4547,7 +4554,7 @@ static void ModList_RebuildList(void)
                description = FS_CheckGameDir(list.strings[i]);
                if (description == NULL || description == fs_checkgamedir_missing) continue;
 
-               strlcpy (modlist[modlist_count].dir, list.strings[i], sizeof(modlist[modlist_count].dir));
+               dp_strlcpy (modlist[modlist_count].dir, list.strings[i], sizeof(modlist[modlist_count].dir));
                //check currently loaded mods
                modlist[modlist_count].loaded = false;
                if (fs_numgamedirs)
@@ -4573,7 +4580,7 @@ static void ModList_Enable (void)
        // copy our mod list into an array for FS_ChangeGameDirs
        numgamedirs = modlist_numenabled;
        for (i = 0; i < modlist_numenabled; i++)
-               strlcpy (gamedirs[i], modlist[modlist_enabled[i]].dir,sizeof (gamedirs[i]));
+               dp_strlcpy (gamedirs[i], modlist[modlist_enabled[i]].dir,sizeof (gamedirs[i]));
 
        // this code snippet is from FS_ChangeGameDirs
        if (fs_numgamedirs == numgamedirs)
@@ -4602,7 +4609,7 @@ void M_Menu_ModList_f(cmd_state_t *cmd)
        m_state = m_modlist;
        m_entersound = true;
        modlist_cursor = 0;
-       M_Update_Return_Reason("");
+       cl_connect_status[0] = '\0';
        ModList_RebuildList();
 }
 
@@ -4652,8 +4659,8 @@ static void M_ModList_Draw (void)
        for (y = 0; y < modlist_numenabled; y++)
                M_PrintRed(432, 48 + y * 8, modlist[modlist_enabled[y]].dir);
 
-       if (*m_return_reason)
-               M_Print(16, menu_height - 8, m_return_reason);
+       if (*cl_connect_status)
+               M_Print(16, menu_height - 8, cl_connect_status);
        // scroll the list as the cursor moves
        y = 48;
        visible = (int)((menu_height - 16 - y) / 8 / 2);
@@ -5031,15 +5038,22 @@ void M_Shutdown(void)
 //============================================================================
 // Menu prog handling
 
-static const char *m_required_func[] = {
-"m_init",
-"m_keydown",
-"m_draw",
-"m_toggle",
-"m_shutdown",
-};
+static void MP_CheckRequiredFuncs(prvm_prog_t *prog, const char *filename)
+{
+       int i;
+       const char *m_required_func[] = {
+               "m_init",
+               "m_keydown",
+               "m_draw",
+               "m_toggle",
+               "m_shutdown",
+       };
+       int m_numrequiredfunc = sizeof(m_required_func) / sizeof(char*);
 
-static int m_numrequiredfunc = sizeof(m_required_func) / sizeof(char*);
+       for(i = 0; i < m_numrequiredfunc; ++i)
+               if(PRVM_ED_FindFunction(prog, m_required_func[i]) == 0)
+                       prog->error_cmd("%s: %s not found in %s",prog->name, m_required_func[i], filename);
+}
 
 static prvm_required_field_t m_required_fields[] =
 {
@@ -5204,41 +5218,45 @@ void MR_SetRouting (qbool forceold);
 void MVM_error_cmd(const char *format, ...) DP_FUNC_PRINTF(1);
 void MVM_error_cmd(const char *format, ...)
 {
-       prvm_prog_t *prog = MVM_prog;
        static qbool processingError = false;
        char errorstring[MAX_INPUTLINE];
        va_list argptr;
+       int outfd = sys.outfd;
+
+       // set output to stderr
+       sys.outfd = fileno(stderr);
 
        va_start (argptr, format);
        dpvsnprintf (errorstring, sizeof(errorstring), format, argptr);
        va_end (argptr);
 
        if (host.framecount < 3)
-               Sys_Error("Menu_Error: %s\n", errorstring);
+               Sys_Error("Menu_Error: %s", errorstring);
 
-       Con_Printf( "Menu_Error: %s\n", errorstring );
+       Con_Printf(CON_ERROR "Menu_Error: %s\n", errorstring);
 
-       if( !processingError ) {
+       if(!processingError)
+       {
                processingError = true;
-               PRVM_Crash(prog);
+               PRVM_Crash();
                processingError = false;
-       } else {
-               Con_Printf( "Menu_Error: Recursive call to MVM_error_cmd (from PRVM_Crash)!\n" );
        }
+       else
+               Sys_Error("Menu_Error: Recursive call to MVM_error_cmd (from PRVM_Crash)!");
 
-       // fall back to the normal menu
-
-       // say it
-       Con_Print("Falling back to normal menu\n");
-
+       Con_Print("Falling back to engine menu\n");
        key_dest = key_game;
-
-       // init the normal menu now -> this will also correct the menu router pointers
        MR_SetRouting (true);
 
        // reset the active scene, too (to be on the safe side ;))
        R_SelectScene( RST_CLIENT );
 
+       // prevent an endless loop if the error was triggered by a command
+       Cbuf_Clear(cmd_local->cbuf);
+
+       // restore configured outfd
+       sys.outfd = outfd;
+
        // Let video start at least
        Host_AbortCurrentFrame();
 }
@@ -5299,10 +5317,12 @@ static void MP_KeyEvent (int key, int ascii, qbool downevent)
 static void MP_Draw (void)
 {
        prvm_prog_t *prog = MVM_prog;
-       // declarations that are needed right now
-
        float oldquality;
 
+       // don't crash if we draw a frame between prog shutdown and restart, see Host_LoadConfig_f
+       if (!prog->loaded)
+               return;
+
        R_SelectScene( RST_MENU );
 
        // reset the temp entities each frame
@@ -5403,7 +5423,7 @@ static void MP_Init (void)
        // allocate the mempools
        prog->progs_mempool = Mem_AllocPool(menu_progs.string, 0, NULL);
 
-       PRVM_Prog_Load(prog, menu_progs.string, NULL, 0, m_numrequiredfunc, m_required_func, m_numrequiredfields, m_required_fields, m_numrequiredglobals, m_required_globals);
+       PRVM_Prog_Load(prog, menu_progs.string, NULL, 0, MP_CheckRequiredFuncs, m_numrequiredfields, m_required_fields, m_numrequiredglobals, m_required_globals);
 
        // note: OP_STATE is not supported by menu qc, we don't even try to detect
        // it here
diff --git a/menu.h b/menu.h
index 38a034f6629b1d419dbc6b996bca5c025e184e31..2344f44434da5b0338e787db258f8b9d7a297ab7 100644 (file)
--- a/menu.h
+++ b/menu.h
@@ -52,8 +52,7 @@ enum m_state_e {
 };
 
 extern enum m_state_e m_state;
-extern char m_return_reason[128];
-void M_Update_Return_Reason(const char *s);
+
 
 /*
 // hard-coded menus
@@ -72,7 +71,7 @@ void MP_Draw (void);
 void MP_ToggleMenu (int mode);
 void MP_Shutdown (void);*/
 
-qbool MP_ConsoleCommand(const char *text);
+qbool MP_ConsoleCommand(const char *text, size_t textlen);
 
 //
 // menu router
index 11ac0d2830e1a0f3d556d1954824c4baaba4f530..a18de3095dcc498534dbdb54ec979da5ef7a5698 100644 (file)
@@ -904,7 +904,7 @@ static void Mod_MDL_LoadFrames (unsigned char* datapointer, int inverts, int *ve
                // get scene name from first frame
                pinframe = (daliasframe_t *)datapointer;
 
-               strlcpy(scene->name, pinframe->name, sizeof(scene->name));
+               dp_strlcpy(scene->name, pinframe->name, sizeof(scene->name));
                scene->firstframe = pose;
                scene->framecount = groupframes;
                scene->framerate = 1.0f / interval;
@@ -1041,7 +1041,7 @@ void Mod_IDP0_Load(model_t *mod, void *buffer, void *bufferend)
        BOUNDI((int)loadmodel->synctype,0,2);
        // convert model flags to EF flags (MF_ROCKET becomes EF_ROCKET, etc)
        i = LittleLong (pinmodel->flags);
-       loadmodel->effects = ((i & 255) << 24) | (i & 0x00FFFF00);
+       loadmodel->effects = (((unsigned)i & 255) << 24) | (i & 0x00FFFF00);
 
        if (strstr(r_nolerp_list.string, loadmodel->name))
                loadmodel->nolerp = true;
@@ -1281,7 +1281,7 @@ void Mod_IDP0_Load(model_t *mod, void *buffer, void *bufferend)
 
                        // store the info about the new skin
                        Mod_LoadCustomMaterial(loadmodel->mempool, loadmodel->data_textures + totalskins * loadmodel->num_surfaces, name, SUPERCONTENTS_SOLID, MATERIALFLAG_WALL, tempskinframe);
-                       strlcpy(loadmodel->skinscenes[loadmodel->numskins].name, name, sizeof(loadmodel->skinscenes[loadmodel->numskins].name));
+                       dp_strlcpy(loadmodel->skinscenes[loadmodel->numskins].name, name, sizeof(loadmodel->skinscenes[loadmodel->numskins].name));
                        loadmodel->skinscenes[loadmodel->numskins].firstframe = totalskins;
                        loadmodel->skinscenes[loadmodel->numskins].framecount = 1;
                        loadmodel->skinscenes[loadmodel->numskins].framerate = 10.0f;
@@ -1542,7 +1542,7 @@ void Mod_IDP2_Load(model_t *mod, void *buffer, void *bufferend)
                        out[k] = v[vertremap[k]];
                datapointer += numxyz * sizeof(trivertx_t);
 
-               strlcpy(loadmodel->animscenes[i].name, pinframe->name, sizeof(loadmodel->animscenes[i].name));
+               dp_strlcpy(loadmodel->animscenes[i].name, pinframe->name, sizeof(loadmodel->animscenes[i].name));
                loadmodel->animscenes[i].firstframe = i;
                loadmodel->animscenes[i].framecount = 1;
                loadmodel->animscenes[i].framerate = 10;
@@ -1616,7 +1616,7 @@ void Mod_IDP3_Load(model_t *mod, void *buffer, void *bufferend)
        loadmodel->synctype = ST_RAND;
        // convert model flags to EF flags (MF_ROCKET becomes EF_ROCKET, etc)
        i = LittleLong (pinmodel->flags);
-       loadmodel->effects = ((i & 255) << 24) | (i & 0x00FFFF00);
+       loadmodel->effects = (((unsigned)i & 255) << 24) | (i & 0x00FFFF00);
 
        // set up some global info about the model
        loadmodel->numframes = LittleLong(pinmodel->num_frames);
@@ -1636,7 +1636,7 @@ void Mod_IDP3_Load(model_t *mod, void *buffer, void *bufferend)
        loadmodel->animscenes = (animscene_t *)Mem_Alloc(loadmodel->mempool, loadmodel->numframes * sizeof(animscene_t));
        for (i = 0, pinframe = (md3frameinfo_t *)((unsigned char *)pinmodel + LittleLong(pinmodel->lump_frameinfo));i < loadmodel->numframes;i++, pinframe++)
        {
-               strlcpy(loadmodel->animscenes[i].name, pinframe->name, sizeof(loadmodel->animscenes[i].name));
+               dp_strlcpy(loadmodel->animscenes[i].name, pinframe->name, sizeof(loadmodel->animscenes[i].name));
                loadmodel->animscenes[i].firstframe = i;
                loadmodel->animscenes[i].framecount = 1;
                loadmodel->animscenes[i].framerate = 10;
@@ -1649,7 +1649,7 @@ void Mod_IDP3_Load(model_t *mod, void *buffer, void *bufferend)
        loadmodel->data_tags = (aliastag_t *)Mem_Alloc(loadmodel->mempool, loadmodel->num_tagframes * loadmodel->num_tags * sizeof(aliastag_t));
        for (i = 0, pintag = (md3tag_t *)((unsigned char *)pinmodel + LittleLong(pinmodel->lump_tags));i < loadmodel->num_tagframes * loadmodel->num_tags;i++, pintag++)
        {
-               strlcpy(loadmodel->data_tags[i].name, pintag->name, sizeof(loadmodel->data_tags[i].name));
+               dp_strlcpy(loadmodel->data_tags[i].name, pintag->name, sizeof(loadmodel->data_tags[i].name));
                for (j = 0;j < 9;j++)
                        loadmodel->data_tags[i].matrixgl[j] = LittleFloat(pintag->rotationmatrix[j]);
                for (j = 0;j < 3;j++)
@@ -1674,21 +1674,31 @@ void Mod_IDP3_Load(model_t *mod, void *buffer, void *bufferend)
        loadmodel->submodelsurfaces_end = loadmodel->num_surfaces;
        loadmodel->num_textures = loadmodel->num_surfaces * loadmodel->numskins;
        loadmodel->num_texturesperskin = loadmodel->num_surfaces;
-       data = (unsigned char *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * sizeof(msurface_t) + loadmodel->num_surfaces * sizeof(int) + loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t) + meshtriangles * sizeof(int[3]) + (meshvertices <= 65536 ? meshtriangles * sizeof(unsigned short[3]) : 0) + meshvertices * sizeof(float[2]) + meshvertices * loadmodel->numframes * sizeof(md3vertex_t));
-       loadmodel->data_surfaces = (msurface_t *)data;data += loadmodel->num_surfaces * sizeof(msurface_t);
-       loadmodel->modelsurfaces_sorted = (int *)data;data += loadmodel->num_surfaces * sizeof(int);
-       loadmodel->data_textures = (texture_t *)data;data += loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t);
        loadmodel->surfmesh.num_vertices = meshvertices;
        loadmodel->surfmesh.num_triangles = meshtriangles;
        loadmodel->surfmesh.num_morphframes = loadmodel->numframes; // TODO: remove?
        loadmodel->num_poses = loadmodel->surfmesh.num_morphframes;
-       loadmodel->surfmesh.data_element3i = (int *)data;data += meshtriangles * sizeof(int[3]);
-       loadmodel->surfmesh.data_texcoordtexture2f = (float *)data;data += meshvertices * sizeof(float[2]);
-       loadmodel->surfmesh.data_morphmd3vertex = (md3vertex_t *)data;data += meshvertices * loadmodel->numframes * sizeof(md3vertex_t);
+
+       // do most allocations as one merged chunk
+       // This is only robust for C standard types!
+       data = (unsigned char *)Mem_Alloc(loadmodel->mempool,
+               meshvertices * sizeof(float[2])
+               + loadmodel->num_surfaces * sizeof(int)
+               + meshtriangles * sizeof(int[3])
+               + (meshvertices <= 65536 ? meshtriangles * sizeof(unsigned short[3]) : 0));
+       // Pointers must be taken in descending order of alignment requirement!
+       loadmodel->surfmesh.data_texcoordtexture2f        = (float *)data; data += meshvertices * sizeof(float[2]);
+       loadmodel->modelsurfaces_sorted                     = (int *)data; data += loadmodel->num_surfaces * sizeof(int);
+       loadmodel->surfmesh.data_element3i                  = (int *)data; data += meshtriangles * sizeof(int[3]);
        if (meshvertices <= 65536)
        {
-               loadmodel->surfmesh.data_element3s = (unsigned short *)data;data += meshtriangles * sizeof(unsigned short[3]);
+               loadmodel->surfmesh.data_element3s = (unsigned short *)data; data += meshtriangles * sizeof(unsigned short[3]);
        }
+       // Struct alignment requirements could change so we can't assume them here
+       // otherwise a safe-looking commit could introduce undefined behaviour!
+       loadmodel->data_surfaces = Mem_AllocType(loadmodel->mempool, msurface_t, loadmodel->num_surfaces * sizeof(msurface_t));
+       loadmodel->data_textures = Mem_AllocType(loadmodel->mempool, texture_t, loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t));
+       loadmodel->surfmesh.data_morphmd3vertex = Mem_AllocType(loadmodel->mempool, md3vertex_t, meshvertices * loadmodel->numframes * sizeof(md3vertex_t));
 
        meshvertices = 0;
        meshtriangles = 0;
@@ -1931,29 +1941,41 @@ void Mod_ZYMOTICMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        loadmodel->submodelsurfaces_end = loadmodel->num_surfaces;
        loadmodel->num_textures = loadmodel->num_surfaces * loadmodel->numskins;
        loadmodel->num_texturesperskin = loadmodel->num_surfaces;
-       data = (unsigned char *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * sizeof(msurface_t) + loadmodel->num_surfaces * sizeof(int) + loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t) + meshtriangles * sizeof(int[3]) + (meshvertices <= 65536 ? meshtriangles * sizeof(unsigned short[3]) : 0) + meshvertices * (sizeof(float[14]) + sizeof(unsigned short) + sizeof(unsigned char[2][4])) + loadmodel->num_poses * loadmodel->num_bones * sizeof(short[7]) + loadmodel->num_bones * sizeof(float[12]));
-       loadmodel->data_surfaces = (msurface_t *)data;data += loadmodel->num_surfaces * sizeof(msurface_t);
-       loadmodel->modelsurfaces_sorted = (int *)data;data += loadmodel->num_surfaces * sizeof(int);
-       loadmodel->data_textures = (texture_t *)data;data += loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t);
        loadmodel->surfmesh.num_vertices = meshvertices;
        loadmodel->surfmesh.num_triangles = meshtriangles;
-       loadmodel->surfmesh.data_element3i = (int *)data;data += meshtriangles * sizeof(int[3]);
-       loadmodel->surfmesh.data_vertex3f = (float *)data;data += meshvertices * sizeof(float[3]);
-       loadmodel->surfmesh.data_svector3f = (float *)data;data += meshvertices * sizeof(float[3]);
-       loadmodel->surfmesh.data_tvector3f = (float *)data;data += meshvertices * sizeof(float[3]);
-       loadmodel->surfmesh.data_normal3f = (float *)data;data += meshvertices * sizeof(float[3]);
-       loadmodel->surfmesh.data_texcoordtexture2f = (float *)data;data += meshvertices * sizeof(float[2]);
-       loadmodel->surfmesh.data_skeletalindex4ub = (unsigned char *)data;data += meshvertices * sizeof(unsigned char[4]);
-       loadmodel->surfmesh.data_skeletalweight4ub = (unsigned char *)data;data += meshvertices * sizeof(unsigned char[4]);
-       loadmodel->data_baseboneposeinverse = (float *)data;data += loadmodel->num_bones * sizeof(float[12]);
        loadmodel->surfmesh.num_blends = 0;
-       loadmodel->surfmesh.blends = (unsigned short *)data;data += meshvertices * sizeof(unsigned short);
+       loadmodel->surfmesh.data_blendweights = NULL;
+
+       // do most allocations as one merged chunk
+       // This is only robust for C standard types!
+       data = (unsigned char *)Mem_Alloc(loadmodel->mempool,
+               loadmodel->num_surfaces * sizeof(int)
+               + meshtriangles * sizeof(int[3])
+               + (meshvertices <= 65536 ? loadmodel->surfmesh.num_triangles * sizeof(unsigned short[3]) : 0)
+               + meshvertices * (sizeof(float[14]) + sizeof(unsigned short) + sizeof(unsigned char[2][4]))
+               + loadmodel->num_poses * loadmodel->num_bones * sizeof(short[7])
+               + loadmodel->num_bones * sizeof(float[12]));
+       // Pointers must be taken in descending order of alignment requirement!
+       loadmodel->surfmesh.data_vertex3f          = (float *)data; data += meshvertices * sizeof(float[3]);
+       loadmodel->surfmesh.data_svector3f         = (float *)data; data += meshvertices * sizeof(float[3]);
+       loadmodel->surfmesh.data_tvector3f         = (float *)data; data += meshvertices * sizeof(float[3]);
+       loadmodel->surfmesh.data_normal3f          = (float *)data; data += meshvertices * sizeof(float[3]);
+       loadmodel->surfmesh.data_texcoordtexture2f = (float *)data; data += meshvertices * sizeof(float[2]);
+       loadmodel->data_baseboneposeinverse        = (float *)data; data += loadmodel->num_bones * sizeof(float[12]);
+       loadmodel->modelsurfaces_sorted              = (int *)data; data += loadmodel->num_surfaces * sizeof(int);
+       loadmodel->surfmesh.data_element3i           = (int *)data; data += meshtriangles * sizeof(int[3]);
+       loadmodel->surfmesh.blends        = (unsigned short *)data; data += meshvertices * sizeof(unsigned short);
        if (loadmodel->surfmesh.num_vertices <= 65536)
        {
                loadmodel->surfmesh.data_element3s = (unsigned short *)data;data += loadmodel->surfmesh.num_triangles * sizeof(unsigned short[3]);
        }
-       loadmodel->data_poses7s = (short *)data;data += loadmodel->num_poses * loadmodel->num_bones * sizeof(short[7]);
-       loadmodel->surfmesh.data_blendweights = NULL;
+       loadmodel->data_poses7s                            = (short *)data; data += loadmodel->num_poses * loadmodel->num_bones * sizeof(short[7]);
+       loadmodel->surfmesh.data_skeletalindex4ub  = (unsigned char *)data; data += meshvertices * sizeof(unsigned char[4]);
+       loadmodel->surfmesh.data_skeletalweight4ub = (unsigned char *)data; data += meshvertices * sizeof(unsigned char[4]);
+       // Struct alignment requirements could change so we can't assume them here
+       // otherwise a safe-looking commit could introduce undefined behaviour!
+       loadmodel->data_surfaces = Mem_AllocType(loadmodel->mempool, msurface_t, loadmodel->num_surfaces * sizeof(msurface_t));
+       loadmodel->data_textures = Mem_AllocType(loadmodel->mempool, texture_t, loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t));
 
        //zymlump_t lump_poses; // float pose[numposes][numbones][3][4]; // animation data
        poses = (float *) (pheader->lump_poses.start + pbase);
@@ -2245,33 +2267,44 @@ void Mod_DARKPLACESMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        loadmodel->submodelsurfaces_end = loadmodel->num_surfaces = pheader->num_meshs;
        loadmodel->num_textures = loadmodel->num_surfaces * loadmodel->numskins;
        loadmodel->num_texturesperskin = loadmodel->num_surfaces;
-       // do most allocations as one merged chunk
-       data = (unsigned char *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * sizeof(msurface_t) + loadmodel->num_surfaces * sizeof(int) + loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t) + meshtriangles * sizeof(int[3]) + (meshvertices <= 65536 ? meshtriangles * sizeof(unsigned short[3]) : 0) + meshvertices * (sizeof(float[14]) + sizeof(unsigned short) + sizeof(unsigned char[2][4])) + loadmodel->num_poses * loadmodel->num_bones * sizeof(short[7]) + loadmodel->num_bones * sizeof(float[12]) + loadmodel->numskins * sizeof(animscene_t) + loadmodel->num_bones * sizeof(aliasbone_t) + loadmodel->numframes * sizeof(animscene_t));
-       loadmodel->data_surfaces = (msurface_t *)data;data += loadmodel->num_surfaces * sizeof(msurface_t);
-       loadmodel->modelsurfaces_sorted = (int *)data;data += loadmodel->num_surfaces * sizeof(int);
-       loadmodel->data_textures = (texture_t *)data;data += loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t);
        loadmodel->surfmesh.num_vertices = meshvertices;
        loadmodel->surfmesh.num_triangles = meshtriangles;
-       loadmodel->surfmesh.data_element3i = (int *)data;data += meshtriangles * sizeof(int[3]);
-       loadmodel->surfmesh.data_vertex3f = (float *)data;data += meshvertices * sizeof(float[3]);
-       loadmodel->surfmesh.data_svector3f = (float *)data;data += meshvertices * sizeof(float[3]);
-       loadmodel->surfmesh.data_tvector3f = (float *)data;data += meshvertices * sizeof(float[3]);
-       loadmodel->surfmesh.data_normal3f = (float *)data;data += meshvertices * sizeof(float[3]);
-       loadmodel->surfmesh.data_texcoordtexture2f = (float *)data;data += meshvertices * sizeof(float[2]);
-       loadmodel->surfmesh.data_skeletalindex4ub = (unsigned char *)data;data += meshvertices * sizeof(unsigned char[4]);
-       loadmodel->surfmesh.data_skeletalweight4ub = (unsigned char *)data;data += meshvertices * sizeof(unsigned char[4]);
-       loadmodel->data_baseboneposeinverse = (float *)data;data += loadmodel->num_bones * sizeof(float[12]);
-       loadmodel->skinscenes = (animscene_t *)data;data += loadmodel->numskins * sizeof(animscene_t);
-       loadmodel->data_bones = (aliasbone_t *)data;data += loadmodel->num_bones * sizeof(aliasbone_t);
-       loadmodel->animscenes = (animscene_t *)data;data += loadmodel->numframes * sizeof(animscene_t);
        loadmodel->surfmesh.num_blends = 0;
-       loadmodel->surfmesh.blends = (unsigned short *)data;data += meshvertices * sizeof(unsigned short);
+
+       // do most allocations as one merged chunk
+       // This is only robust for C standard types!
+       data = (unsigned char *)Mem_Alloc(loadmodel->mempool,
+               loadmodel->num_surfaces * sizeof(int)
+               + meshtriangles * sizeof(int[3])
+               + (meshvertices <= 65536 ? meshtriangles * sizeof(unsigned short[3]) : 0)
+               + meshvertices * (sizeof(float[14]) + sizeof(unsigned short) + sizeof(unsigned char[2][4]))
+               + loadmodel->num_poses * loadmodel->num_bones * sizeof(short[7])
+               + loadmodel->num_bones * sizeof(float[12]));
+       // Pointers must be taken in descending order of alignment requirement!
+       loadmodel->surfmesh.data_vertex3f                  = (float *)data; data += meshvertices * sizeof(float[3]);
+       loadmodel->surfmesh.data_svector3f                 = (float *)data; data += meshvertices * sizeof(float[3]);
+       loadmodel->surfmesh.data_tvector3f                 = (float *)data; data += meshvertices * sizeof(float[3]);
+       loadmodel->surfmesh.data_normal3f                  = (float *)data; data += meshvertices * sizeof(float[3]);
+       loadmodel->surfmesh.data_texcoordtexture2f         = (float *)data; data += meshvertices * sizeof(float[2]);
+       loadmodel->data_baseboneposeinverse                = (float *)data; data += loadmodel->num_bones * sizeof(float[12]);
+       loadmodel->modelsurfaces_sorted                      = (int *)data; data += loadmodel->num_surfaces * sizeof(int);
+       loadmodel->surfmesh.data_element3i                   = (int *)data; data += meshtriangles * sizeof(int[3]);
+       loadmodel->surfmesh.blends                = (unsigned short *)data; data += meshvertices * sizeof(unsigned short);
        if (meshvertices <= 65536)
        {
-               loadmodel->surfmesh.data_element3s = (unsigned short *)data;data += meshtriangles * sizeof(unsigned short[3]);
+               loadmodel->surfmesh.data_element3s = (unsigned short *)data; data += meshtriangles * sizeof(unsigned short[3]);
        }
-       loadmodel->data_poses7s = (short *)data;data += loadmodel->num_poses * loadmodel->num_bones * sizeof(short[7]);
-       loadmodel->surfmesh.data_blendweights = (blendweights_t *)Mem_Alloc(loadmodel->mempool, meshvertices * sizeof(blendweights_t));
+       loadmodel->data_poses7s                            = (short *)data; data += loadmodel->num_poses * loadmodel->num_bones * sizeof(short[7]);
+       loadmodel->surfmesh.data_skeletalindex4ub  = (unsigned char *)data; data += meshvertices * sizeof(unsigned char[4]);
+       loadmodel->surfmesh.data_skeletalweight4ub = (unsigned char *)data; data += meshvertices * sizeof(unsigned char[4]);
+       // Struct alignment requirements could change so we can't assume them here
+       // otherwise a safe-looking commit could introduce undefined behaviour!
+       loadmodel->data_surfaces = Mem_AllocType(loadmodel->mempool, msurface_t, loadmodel->num_surfaces * sizeof(msurface_t));
+       loadmodel->data_textures = Mem_AllocType(loadmodel->mempool, texture_t, loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t));
+       loadmodel->skinscenes = Mem_AllocType(loadmodel->mempool, animscene_t, loadmodel->numskins * sizeof(animscene_t));
+       loadmodel->data_bones = Mem_AllocType(loadmodel->mempool, aliasbone_t, loadmodel->num_bones * sizeof(aliasbone_t));
+       loadmodel->animscenes = Mem_AllocType(loadmodel->mempool, animscene_t, loadmodel->numframes * sizeof(animscene_t));
+       loadmodel->surfmesh.data_blendweights = Mem_AllocType(loadmodel->mempool, blendweights_t, meshvertices * sizeof(blendweights_t));
 
        for (i = 0;i < loadmodel->numskins;i++)
        {
@@ -2478,7 +2511,7 @@ void Mod_DARKPLACESMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                Mod_ValidateElements(loadmodel->surfmesh.data_element3i + surface->num_firsttriangle * 3, loadmodel->surfmesh.data_element3s + surface->num_firsttriangle * 3, surface->num_triangles, surface->num_firstvertex, surface->num_vertices, __FILE__, __LINE__);
        }
        if (loadmodel->surfmesh.num_blends < meshvertices)
-               loadmodel->surfmesh.data_blendweights = (blendweights_t *)Mem_Realloc(loadmodel->mempool, loadmodel->surfmesh.data_blendweights, loadmodel->surfmesh.num_blends * sizeof(blendweights_t));
+               loadmodel->surfmesh.data_blendweights = Mem_ReallocType(loadmodel->mempool, loadmodel->surfmesh.data_blendweights, blendweights_t, loadmodel->surfmesh.num_blends * sizeof(blendweights_t));
        Z_Free(bonepose);
        Mod_FreeSkinFiles(skinfiles);
        Mod_MakeSortedSurfaces(loadmodel);
@@ -2523,7 +2556,6 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        pskchunk_t *pchunk;
        skinfile_t *skinfiles;
        char animname[MAX_QPATH];
-       size_t size;
        float biggestorigin;
 
        pchunk = (pskchunk_t *)buffer;
@@ -2547,7 +2579,7 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        loadmodel->synctype = ST_RAND;
 
        FS_StripExtension(loadmodel->name, animname, sizeof(animname));
-       strlcat(animname, ".psa", sizeof(animname));
+       dp_strlcat(animname, ".psa", sizeof(animname));
        animbuffer = animfilebuffer = FS_LoadFile(animname, loadmodel->mempool, false, &filesize);
        animbufferend = (void *)((unsigned char*)animbuffer + (int)filesize);
        if (!animbuffer)
@@ -2898,32 +2930,49 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        loadmodel->num_texturesperskin = loadmodel->num_surfaces;
        loadmodel->surfmesh.num_vertices = meshvertices;
        loadmodel->surfmesh.num_triangles = meshtriangles;
+
        // do most allocations as one merged chunk
-       size = loadmodel->num_surfaces * sizeof(msurface_t) + loadmodel->num_surfaces * sizeof(int) + loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t) + loadmodel->surfmesh.num_triangles * sizeof(int[3]) + loadmodel->surfmesh.num_vertices * sizeof(float[3]) + loadmodel->surfmesh.num_vertices * sizeof(float[3]) + loadmodel->surfmesh.num_vertices * sizeof(float[3]) + loadmodel->surfmesh.num_vertices * sizeof(float[3]) + loadmodel->surfmesh.num_vertices * sizeof(float[2]) + loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]) + loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]) + loadmodel->surfmesh.num_vertices * sizeof(unsigned short) + loadmodel->num_poses * loadmodel->num_bones * sizeof(short[7]) + loadmodel->num_bones * sizeof(float[12]) + loadmodel->numskins * sizeof(animscene_t) + loadmodel->num_bones * sizeof(aliasbone_t) + loadmodel->numframes * sizeof(animscene_t) + ((loadmodel->surfmesh.num_vertices <= 65536) ? (loadmodel->surfmesh.num_triangles * sizeof(unsigned short[3])) : 0);
-       data = (unsigned char *)Mem_Alloc(loadmodel->mempool, size);
-       loadmodel->data_surfaces = (msurface_t *)data;data += loadmodel->num_surfaces * sizeof(msurface_t);
-       loadmodel->modelsurfaces_sorted = (int *)data;data += loadmodel->num_surfaces * sizeof(int);
-       loadmodel->data_textures = (texture_t *)data;data += loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t);
-       loadmodel->surfmesh.data_element3i = (int *)data;data += loadmodel->surfmesh.num_triangles * sizeof(int[3]);
-       loadmodel->surfmesh.data_vertex3f = (float *)data;data += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
-       loadmodel->surfmesh.data_svector3f = (float *)data;data += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
-       loadmodel->surfmesh.data_tvector3f = (float *)data;data += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
-       loadmodel->surfmesh.data_normal3f = (float *)data;data += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
-       loadmodel->surfmesh.data_texcoordtexture2f = (float *)data;data += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
-       loadmodel->surfmesh.data_skeletalindex4ub = (unsigned char *)data;data += loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]);
-       loadmodel->surfmesh.data_skeletalweight4ub = (unsigned char *)data;data += loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]);
-       loadmodel->data_baseboneposeinverse = (float *)data;data += loadmodel->num_bones * sizeof(float[12]);
-       loadmodel->skinscenes = (animscene_t *)data;data += loadmodel->numskins * sizeof(animscene_t);
-       loadmodel->data_bones = (aliasbone_t *)data;data += loadmodel->num_bones * sizeof(aliasbone_t);
-       loadmodel->animscenes = (animscene_t *)data;data += loadmodel->numframes * sizeof(animscene_t);
+       // This is only robust for C standard types!
+       data = (unsigned char *)Mem_Alloc(loadmodel->mempool,
+               loadmodel->surfmesh.num_vertices * sizeof(float[3])
+               + loadmodel->surfmesh.num_vertices * sizeof(float[3])
+               + loadmodel->surfmesh.num_vertices * sizeof(float[3])
+               + loadmodel->surfmesh.num_vertices * sizeof(float[3])
+               + loadmodel->surfmesh.num_vertices * sizeof(float[2])
+               + loadmodel->num_bones * sizeof(float[12])
+               + loadmodel->num_surfaces * sizeof(int)
+               + loadmodel->surfmesh.num_triangles * sizeof(int[3])
+               + loadmodel->surfmesh.num_vertices * sizeof(unsigned short)
+               + ((loadmodel->surfmesh.num_vertices <= 65536) ? (loadmodel->surfmesh.num_triangles * sizeof(unsigned short[3])) : 0)
+               + loadmodel->num_poses * loadmodel->num_bones * sizeof(short[7])
+               + loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4])
+               + loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]));
+       // Pointers must be taken in descending order of alignment requirement!
+       loadmodel->surfmesh.data_vertex3f          = (float *)data; data += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
+       loadmodel->surfmesh.data_svector3f         = (float *)data; data += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
+       loadmodel->surfmesh.data_tvector3f         = (float *)data; data += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
+       loadmodel->surfmesh.data_normal3f          = (float *)data; data += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
+       loadmodel->surfmesh.data_texcoordtexture2f = (float *)data; data += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
+       loadmodel->data_baseboneposeinverse        = (float *)data; data += loadmodel->num_bones * sizeof(float[12]);
+       loadmodel->modelsurfaces_sorted              = (int *)data; data += loadmodel->num_surfaces * sizeof(int);
+       loadmodel->surfmesh.data_element3i           = (int *)data; data += loadmodel->surfmesh.num_triangles * sizeof(int[3]);
        loadmodel->surfmesh.num_blends = 0;
-       loadmodel->surfmesh.blends = (unsigned short *)data;data += meshvertices * sizeof(unsigned short);
+       loadmodel->surfmesh.blends        = (unsigned short *)data; data += loadmodel->surfmesh.num_vertices * sizeof(unsigned short);
        if (loadmodel->surfmesh.num_vertices <= 65536)
        {
                loadmodel->surfmesh.data_element3s = (unsigned short *)data;data += loadmodel->surfmesh.num_triangles * sizeof(unsigned short[3]);
        }
-       loadmodel->data_poses7s = (short *)data;data += loadmodel->num_poses * loadmodel->num_bones * sizeof(short[7]);
-       loadmodel->surfmesh.data_blendweights = (blendweights_t *)Mem_Alloc(loadmodel->mempool, loadmodel->surfmesh.num_vertices * sizeof(blendweights_t));
+       loadmodel->data_poses7s                            = (short *)data; data += loadmodel->num_poses * loadmodel->num_bones * sizeof(short[7]);
+       loadmodel->surfmesh.data_skeletalindex4ub  = (unsigned char *)data; data += loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]);
+       loadmodel->surfmesh.data_skeletalweight4ub = (unsigned char *)data; data += loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]);
+       // Struct alignment requirements could change so we can't assume them here
+       // otherwise a safe-looking commit could introduce undefined behaviour!
+       loadmodel->data_surfaces = Mem_AllocType(loadmodel->mempool, msurface_t, loadmodel->num_surfaces * sizeof(msurface_t));
+       loadmodel->data_textures = Mem_AllocType(loadmodel->mempool, texture_t, loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t));
+       loadmodel->skinscenes = Mem_AllocType(loadmodel->mempool, animscene_t, loadmodel->numskins * sizeof(animscene_t));
+       loadmodel->data_bones = Mem_AllocType(loadmodel->mempool, aliasbone_t, loadmodel->num_bones * sizeof(aliasbone_t));
+       loadmodel->animscenes = Mem_AllocType(loadmodel->mempool, animscene_t, loadmodel->numframes * sizeof(animscene_t));
+       loadmodel->surfmesh.data_blendweights = Mem_AllocType(loadmodel->mempool, blendweights_t, loadmodel->surfmesh.num_vertices * sizeof(blendweights_t));
 
        for (i = 0;i < loadmodel->numskins;i++)
        {
@@ -2974,7 +3023,7 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        // copy over the bones
        for (index = 0;index < numbones;index++)
        {
-               strlcpy(loadmodel->data_bones[index].name, bones[index].name, sizeof(loadmodel->data_bones[index].name));
+               dp_strlcpy(loadmodel->data_bones[index].name, bones[index].name, sizeof(loadmodel->data_bones[index].name));
                loadmodel->data_bones[index].parent = (index || bones[index].parent > 0) ? bones[index].parent : -1;
                if (loadmodel->data_bones[index].parent >= index)
                        Host_Error("%s bone[%i].parent >= %i", loadmodel->name, index, index);
@@ -3047,7 +3096,7 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                loadmodel->surfmesh.data_skeletalweight4ub[index*4+3] = (unsigned char)(weightinfluence[3]*255.0f);
        }
        if (loadmodel->surfmesh.num_blends < loadmodel->surfmesh.num_vertices)
-               loadmodel->surfmesh.data_blendweights = (blendweights_t *)Mem_Realloc(loadmodel->mempool, loadmodel->surfmesh.data_blendweights, loadmodel->surfmesh.num_blends * sizeof(blendweights_t));
+               loadmodel->surfmesh.data_blendweights = Mem_ReallocType(loadmodel->mempool, loadmodel->surfmesh.data_blendweights, blendweights_t, loadmodel->surfmesh.num_blends * sizeof(blendweights_t));
 
        // set up the animscenes based on the anims
        if (numanims)
@@ -3098,7 +3147,7 @@ void Mod_PSKMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        }
        else
        {
-               strlcpy(loadmodel->animscenes[0].name, "base", sizeof(loadmodel->animscenes[0].name));
+               dp_strlcpy(loadmodel->animscenes[0].name, "base", sizeof(loadmodel->animscenes[0].name));
                loadmodel->animscenes[0].firstframe = 0;
                loadmodel->animscenes[0].framecount = 1;
                loadmodel->animscenes[0].loop = true;
@@ -3174,7 +3223,7 @@ void Mod_INTERQUAKEMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        const unsigned char *pbase, *pend;
        iqmheader_t header;
        skinfile_t *skinfiles;
-       int i, j, k, meshvertices, meshtriangles;
+       int i, j, k;
        float biggestorigin;
        const unsigned int *inelements;
        int *outelements;
@@ -3280,7 +3329,8 @@ void Mod_INTERQUAKEMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                return;
        }
 
-       // copy structs to make them aligned in memory (otherwise we crash on Sparc and PowerPC and others)
+       // Structs will be copied for alignment in memory, otherwise we crash on Sparc, PowerPC and others
+       // and get big spam from UBSan, because these offsets may not be aligned.
        if (header.num_vertexarrays)
                vas = (iqmvertexarray_t *)(pbase + header.ofs_vertexarrays);
        if (header.num_anims)
@@ -3294,11 +3344,13 @@ void Mod_INTERQUAKEMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        {
                iqmvertexarray_t va;
                size_t vsize;
-               va.type = LittleLong(vas[i].type);
-               va.flags = LittleLong(vas[i].flags);
-               va.format = LittleLong(vas[i].format);
-               va.size = LittleLong(vas[i].size);
-               va.offset = LittleLong(vas[i].offset);
+
+               memcpy(&va, &vas[i], sizeof(iqmvertexarray_t));
+               va.type = LittleLong(va.type);
+               va.flags = LittleLong(va.flags);
+               va.format = LittleLong(va.format);
+               va.size = LittleLong(va.size);
+               va.offset = LittleLong(va.offset);
                vsize = header.num_vertexes*va.size;
                switch (va.format)
                { 
@@ -3375,48 +3427,53 @@ void Mod_INTERQUAKEMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        loadmodel->submodelsurfaces_end = loadmodel->num_surfaces = header.num_meshes;
        loadmodel->num_textures = loadmodel->num_surfaces * loadmodel->numskins;
        loadmodel->num_texturesperskin = loadmodel->num_surfaces;
-
-       meshvertices = header.num_vertexes;
-       meshtriangles = header.num_triangles;
+       loadmodel->surfmesh.num_vertices = header.num_vertexes;
+       loadmodel->surfmesh.num_triangles = header.num_triangles;
 
        // do most allocations as one merged chunk
-       data = (unsigned char *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * sizeof(msurface_t) + loadmodel->num_surfaces * sizeof(int) + loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t) + meshtriangles * sizeof(int[3]) + (meshvertices <= 65536 ? meshtriangles * sizeof(unsigned short[3]) : 0) + meshvertices * (sizeof(float[14]) + (vcolor4f || vcolor4ub ? sizeof(float[4]) : 0)) + (vblendindexes && vblendweights ? meshvertices * (sizeof(unsigned short) + sizeof(unsigned char[2][4])) : 0) + loadmodel->num_poses * loadmodel->num_bones * sizeof(short[7]) + loadmodel->num_bones * sizeof(float[12]) + loadmodel->numskins * sizeof(animscene_t) + loadmodel->num_bones * sizeof(aliasbone_t) + loadmodel->numframes * sizeof(animscene_t));
-       loadmodel->data_surfaces = (msurface_t *)data;data += loadmodel->num_surfaces * sizeof(msurface_t);
-       loadmodel->modelsurfaces_sorted = (int *)data;data += loadmodel->num_surfaces * sizeof(int);
-       loadmodel->data_textures = (texture_t *)data;data += loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t);
-       loadmodel->surfmesh.num_vertices = meshvertices;
-       loadmodel->surfmesh.num_triangles = meshtriangles;
-       loadmodel->surfmesh.data_element3i = (int *)data;data += meshtriangles * sizeof(int[3]);
-       loadmodel->surfmesh.data_vertex3f = (float *)data;data += meshvertices * sizeof(float[3]);
-       loadmodel->surfmesh.data_svector3f = (float *)data;data += meshvertices * sizeof(float[3]);
-       loadmodel->surfmesh.data_tvector3f = (float *)data;data += meshvertices * sizeof(float[3]);
-       loadmodel->surfmesh.data_normal3f = (float *)data;data += meshvertices * sizeof(float[3]);
-       loadmodel->surfmesh.data_texcoordtexture2f = (float *)data;data += meshvertices * sizeof(float[2]);
+       // This is only robust for C standard types!
+       data = (unsigned char *)Mem_Alloc(loadmodel->mempool,
+               loadmodel->surfmesh.num_vertices * (sizeof(float[14]) + (vcolor4f || vcolor4ub ? sizeof(float[4]) : 0))
+               + loadmodel->num_bones * sizeof(float[12])
+               + loadmodel->num_surfaces * sizeof(int)
+               + loadmodel->surfmesh.num_triangles * sizeof(int[3])
+               + (loadmodel->surfmesh.num_vertices <= 65536 ? loadmodel->surfmesh.num_triangles * sizeof(unsigned short[3]) : 0)
+               + loadmodel->num_poses * loadmodel->num_bones * sizeof(short[7])
+               + (vblendindexes && vblendweights ? loadmodel->surfmesh.num_vertices * (sizeof(unsigned short) + sizeof(unsigned char[2][4])) : 0));
+       // Pointers must be taken in descending order of alignment requirement!
+       loadmodel->surfmesh.data_vertex3f          = (float *)data; data += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
+       loadmodel->surfmesh.data_svector3f         = (float *)data; data += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
+       loadmodel->surfmesh.data_tvector3f         = (float *)data; data += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
+       loadmodel->surfmesh.data_normal3f          = (float *)data; data += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
+       loadmodel->surfmesh.data_texcoordtexture2f = (float *)data; data += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
        if (vcolor4f || vcolor4ub)
        {
-               loadmodel->surfmesh.data_lightmapcolor4f = (float *)data;data += meshvertices * sizeof(float[4]);
+               loadmodel->surfmesh.data_lightmapcolor4f = (float *)data; data += loadmodel->surfmesh.num_vertices * sizeof(float[4]);
        }
-       if (vblendindexes && vblendweights)
+       loadmodel->data_baseboneposeinverse = (float *)data; data += loadmodel->num_bones * sizeof(float[12]);
+       loadmodel->modelsurfaces_sorted       = (int *)data; data += loadmodel->num_surfaces * sizeof(int);
+       loadmodel->surfmesh.data_element3i    = (int *)data; data += loadmodel->surfmesh.num_triangles * sizeof(int[3]);
+       loadmodel->data_poses7s             = (short *)data; data += loadmodel->num_poses * loadmodel->num_bones * sizeof(short[7]);
+       if (loadmodel->surfmesh.num_vertices <= 65536)
        {
-               loadmodel->surfmesh.data_skeletalindex4ub = (unsigned char *)data;data += meshvertices * sizeof(unsigned char[4]);
-               loadmodel->surfmesh.data_skeletalweight4ub = (unsigned char *)data;data += meshvertices * sizeof(unsigned char[4]);
+               loadmodel->surfmesh.data_element3s = (unsigned short *)data; data += loadmodel->surfmesh.num_triangles * sizeof(unsigned short[3]);
        }
-       loadmodel->data_baseboneposeinverse = (float *)data;data += loadmodel->num_bones * sizeof(float[12]);
-       loadmodel->skinscenes = (animscene_t *)data;data += loadmodel->numskins * sizeof(animscene_t);
-       loadmodel->data_bones = (aliasbone_t *)data;data += loadmodel->num_bones * sizeof(aliasbone_t);
-       loadmodel->animscenes = (animscene_t *)data;data += loadmodel->numframes * sizeof(animscene_t);
        if (vblendindexes && vblendweights)
        {
                loadmodel->surfmesh.num_blends = 0;
-               loadmodel->surfmesh.blends = (unsigned short *)data;data += meshvertices * sizeof(unsigned short);
-       }
-       if (meshvertices <= 65536)
-       {
-               loadmodel->surfmesh.data_element3s = (unsigned short *)data;data += meshtriangles * sizeof(unsigned short[3]);
-       }
-       loadmodel->data_poses7s = (short *)data;data += loadmodel->num_poses * loadmodel->num_bones * sizeof(short[7]);
+               loadmodel->surfmesh.blends                = (unsigned short *)data; data += loadmodel->surfmesh.num_vertices * sizeof(unsigned short);
+               loadmodel->surfmesh.data_skeletalindex4ub  = (unsigned char *)data; data += loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]);
+               loadmodel->surfmesh.data_skeletalweight4ub = (unsigned char *)data; data += loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]);
+       }
+       // Struct alignment requirements could change so we can't assume them here
+       // otherwise a safe-looking commit could introduce undefined behaviour!
+       loadmodel->data_surfaces = Mem_AllocType(loadmodel->mempool, msurface_t, loadmodel->num_surfaces * sizeof(msurface_t));
+       loadmodel->data_textures = Mem_AllocType(loadmodel->mempool, texture_t, loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t));
+       loadmodel->skinscenes = Mem_AllocType(loadmodel->mempool, animscene_t, loadmodel->numskins * sizeof(animscene_t));
+       loadmodel->data_bones = Mem_AllocType(loadmodel->mempool, aliasbone_t, loadmodel->num_bones * sizeof(aliasbone_t));
+       loadmodel->animscenes = Mem_AllocType(loadmodel->mempool, animscene_t, loadmodel->numframes * sizeof(animscene_t));
        if (vblendindexes && vblendweights)
-               loadmodel->surfmesh.data_blendweights = (blendweights_t *)Mem_Alloc(loadmodel->mempool, meshvertices * sizeof(blendweights_t));
+               loadmodel->surfmesh.data_blendweights = Mem_AllocType(loadmodel->mempool, blendweights_t, loadmodel->surfmesh.num_vertices * sizeof(blendweights_t));
 
        for (i = 0;i < loadmodel->numskins;i++)
        {
@@ -3429,21 +3486,25 @@ void Mod_INTERQUAKEMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        // load the bone info
        if (header.version == 1)
        {
-               iqmjoint1_t *injoint1 = (iqmjoint1_t *)(pbase + header.ofs_joints);
+               iqmjoint1_t *injoints1 = (iqmjoint1_t *)(pbase + header.ofs_joints);
+
                if (loadmodel->num_bones)
                        joint1 = (iqmjoint1_t *)Mem_Alloc(loadmodel->mempool, loadmodel->num_bones * sizeof(iqmjoint1_t));
                for (i = 0;i < loadmodel->num_bones;i++)
                {
                        matrix4x4_t relbase, relinvbase, pinvbase, invbase;
-                       joint1[i].name = LittleLong(injoint1[i].name);
-                       joint1[i].parent = LittleLong(injoint1[i].parent);
+                       iqmjoint1_t injoint1;
+
+                       memcpy(&injoint1, &injoints1[i], sizeof(iqmjoint1_t));
+                       joint1[i].name = LittleLong(injoint1.name);
+                       joint1[i].parent = LittleLong(injoint1.parent);
                        for (j = 0;j < 3;j++)
                        {
-                               joint1[i].origin[j] = LittleFloat(injoint1[i].origin[j]);
-                               joint1[i].rotation[j] = LittleFloat(injoint1[i].rotation[j]);
-                               joint1[i].scale[j] = LittleFloat(injoint1[i].scale[j]);
+                               joint1[i].origin[j] = LittleFloat(injoint1.origin[j]);
+                               joint1[i].rotation[j] = LittleFloat(injoint1.rotation[j]);
+                               joint1[i].scale[j] = LittleFloat(injoint1.scale[j]);
                        }
-                       strlcpy(loadmodel->data_bones[i].name, &text[joint1[i].name], sizeof(loadmodel->data_bones[i].name));
+                       dp_strlcpy(loadmodel->data_bones[i].name, &text[joint1[i].name], sizeof(loadmodel->data_bones[i].name));
                        loadmodel->data_bones[i].parent = joint1[i].parent;
                        if (loadmodel->data_bones[i].parent >= i)
                                Host_Error("%s bone[%i].parent >= %i", loadmodel->name, i, i);
@@ -3460,22 +3521,26 @@ void Mod_INTERQUAKEMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        }
        else
        {
-               iqmjoint_t *injoint = (iqmjoint_t *)(pbase + header.ofs_joints);
+               iqmjoint_t *injoints = (iqmjoint_t *)(pbase + header.ofs_joints);
+
                if (header.num_joints)
                        joint = (iqmjoint_t *)Mem_Alloc(loadmodel->mempool, loadmodel->num_bones * sizeof(iqmjoint_t));
                for (i = 0;i < loadmodel->num_bones;i++)
                {
                        matrix4x4_t relbase, relinvbase, pinvbase, invbase;
-                       joint[i].name = LittleLong(injoint[i].name);
-                       joint[i].parent = LittleLong(injoint[i].parent);
+                       iqmjoint_t injoint;
+
+                       memcpy(&injoint, &injoints[i], sizeof(iqmjoint_t));
+                       joint[i].name = LittleLong(injoint.name);
+                       joint[i].parent = LittleLong(injoint.parent);
                        for (j = 0;j < 3;j++)
                        {
-                               joint[i].origin[j] = LittleFloat(injoint[i].origin[j]);
-                               joint[i].rotation[j] = LittleFloat(injoint[i].rotation[j]);
-                               joint[i].scale[j] = LittleFloat(injoint[i].scale[j]);
+                               joint[i].origin[j] = LittleFloat(injoint.origin[j]);
+                               joint[i].rotation[j] = LittleFloat(injoint.rotation[j]);
+                               joint[i].scale[j] = LittleFloat(injoint.scale[j]);
                        }
-                       joint[i].rotation[3] = LittleFloat(injoint[i].rotation[3]);
-                       strlcpy(loadmodel->data_bones[i].name, &text[joint[i].name], sizeof(loadmodel->data_bones[i].name));
+                       joint[i].rotation[3] = LittleFloat(injoint.rotation[3]);
+                       dp_strlcpy(loadmodel->data_bones[i].name, &text[joint[i].name], sizeof(loadmodel->data_bones[i].name));
                        loadmodel->data_bones[i].parent = joint[i].parent;
                        if (loadmodel->data_bones[i].parent >= i)
                                Host_Error("%s bone[%i].parent >= %i", loadmodel->name, i, i);
@@ -3498,12 +3563,14 @@ void Mod_INTERQUAKEMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        for (i = 0;i < (int)header.num_anims;i++)
        {
                iqmanim_t anim;
-               anim.name = LittleLong(anims[i].name);
-               anim.first_frame = LittleLong(anims[i].first_frame);
-               anim.num_frames = LittleLong(anims[i].num_frames);
-               anim.framerate = LittleFloat(anims[i].framerate);
-               anim.flags = LittleLong(anims[i].flags);
-               strlcpy(loadmodel->animscenes[i].name, &text[anim.name], sizeof(loadmodel->animscenes[i].name));
+
+               memcpy(&anim, &anims[i], sizeof(iqmanim_t));
+               anim.name = LittleLong(anim.name);
+               anim.first_frame = LittleLong(anim.first_frame);
+               anim.num_frames = LittleLong(anim.num_frames);
+               anim.framerate = LittleFloat(anim.framerate);
+               anim.flags = LittleLong(anim.flags);
+               dp_strlcpy(loadmodel->animscenes[i].name, &text[anim.name], sizeof(loadmodel->animscenes[i].name));
                loadmodel->animscenes[i].firstframe = anim.first_frame;
                loadmodel->animscenes[i].framecount = anim.num_frames;
                loadmodel->animscenes[i].loop = ((anim.flags & IQM_LOOP) != 0);
@@ -3511,7 +3578,7 @@ void Mod_INTERQUAKEMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        }
        if (header.num_anims <= 0)
        {
-               strlcpy(loadmodel->animscenes[0].name, "static", sizeof(loadmodel->animscenes[0].name));
+               dp_strlcpy(loadmodel->animscenes[0].name, "static", sizeof(loadmodel->animscenes[0].name));
                loadmodel->animscenes[0].firstframe = 0;
                loadmodel->animscenes[0].framecount = 1;
                loadmodel->animscenes[0].loop = true;
@@ -3525,18 +3592,22 @@ void Mod_INTERQUAKEMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        biggestorigin = 0;
        if (header.version == 1)
        {
-               iqmpose1_t *inpose1 = (iqmpose1_t *)(pbase + header.ofs_poses);
+               iqmpose1_t *inposes1 = (iqmpose1_t *)(pbase + header.ofs_poses);
+
                if (header.num_poses)
                        pose1 = (iqmpose1_t *)Mem_Alloc(loadmodel->mempool, header.num_poses * sizeof(iqmpose1_t));
                for (i = 0;i < (int)header.num_poses;i++)
                {
                        float f;
-                       pose1[i].parent = LittleLong(inpose1[i].parent);
-                       pose1[i].channelmask = LittleLong(inpose1[i].channelmask);
+                       iqmpose1_t inpose;
+
+                       memcpy(&inpose, &inposes1[i], sizeof(iqmpose1_t));
+                       pose1[i].parent = LittleLong(inpose.parent);
+                       pose1[i].channelmask = LittleLong(inpose.channelmask);
                        for (j = 0;j < 9;j++)
                        {
-                               pose1[i].channeloffset[j] = LittleFloat(inpose1[i].channeloffset[j]);
-                               pose1[i].channelscale[j] = LittleFloat(inpose1[i].channelscale[j]);
+                               pose1[i].channeloffset[j] = LittleFloat(inpose.channeloffset[j]);
+                               pose1[i].channelscale[j] = LittleFloat(inpose.channelscale[j]);
                        }
                        f = fabs(pose1[i].channeloffset[0]); biggestorigin = max(biggestorigin, f);
                        f = fabs(pose1[i].channeloffset[1]); biggestorigin = max(biggestorigin, f);
@@ -3558,18 +3629,22 @@ void Mod_INTERQUAKEMODEL_Load(model_t *mod, void *buffer, void *bufferend)
        }
        else
        {
-               iqmpose_t *inpose = (iqmpose_t *)(pbase + header.ofs_poses);
+               iqmpose_t *inposes = (iqmpose_t *)(pbase + header.ofs_poses);
+
                if (header.num_poses)
                        pose = (iqmpose_t *)Mem_Alloc(loadmodel->mempool, header.num_poses * sizeof(iqmpose_t));
                for (i = 0;i < (int)header.num_poses;i++)
                {
                        float f;
-                       pose[i].parent = LittleLong(inpose[i].parent);
-                       pose[i].channelmask = LittleLong(inpose[i].channelmask);
+                       iqmpose_t inpose;
+
+                       memcpy(&inpose, &inposes[i], sizeof(iqmpose_t));
+                       pose[i].parent = LittleLong(inpose.parent);
+                       pose[i].channelmask = LittleLong(inpose.channelmask);
                        for (j = 0;j < 10;j++)
                        {
-                               pose[i].channeloffset[j] = LittleFloat(inpose[i].channeloffset[j]);
-                               pose[i].channelscale[j] = LittleFloat(inpose[i].channelscale[j]);
+                               pose[i].channeloffset[j] = LittleFloat(inpose.channeloffset[j]);
+                               pose[i].channelscale[j] = LittleFloat(inpose.channelscale[j]);
                        }
                        f = fabs(pose[i].channeloffset[0]); biggestorigin = max(biggestorigin, f);
                        f = fabs(pose[i].channeloffset[1]); biggestorigin = max(biggestorigin, f);
@@ -3858,12 +3933,13 @@ void Mod_INTERQUAKEMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                iqmmesh_t mesh;
                msurface_t *surface;
 
-               mesh.name = LittleLong(meshes[i].name);
-               mesh.material = LittleLong(meshes[i].material);
-               mesh.first_vertex = LittleLong(meshes[i].first_vertex);
-               mesh.num_vertexes = LittleLong(meshes[i].num_vertexes);
-               mesh.first_triangle = LittleLong(meshes[i].first_triangle);
-               mesh.num_triangles = LittleLong(meshes[i].num_triangles);
+               memcpy(&mesh, &meshes[i], sizeof(iqmmesh_t));
+               mesh.name = LittleLong(mesh.name);
+               mesh.material = LittleLong(mesh.material);
+               mesh.first_vertex = LittleLong(mesh.first_vertex);
+               mesh.num_vertexes = LittleLong(mesh.num_vertexes);
+               mesh.first_triangle = LittleLong(mesh.first_triangle);
+               mesh.num_triangles = LittleLong(mesh.num_triangles);
 
                loadmodel->modelsurfaces_sorted[i] = i;
                surface = loadmodel->data_surfaces + i;
@@ -3898,8 +3974,8 @@ void Mod_INTERQUAKEMODEL_Load(model_t *mod, void *buffer, void *bufferend)
                loadmodel->PointSuperContents = Mod_CollisionBIH_PointSuperContents_Mesh;
        }
 
-       if (joint        ) Mem_Free(joint        );joint         = NULL;
-       if (joint1       ) Mem_Free(joint1       );joint1        = NULL;
-       if (pose         ) Mem_Free(pose         );pose          = NULL;
-       if (pose1        ) Mem_Free(pose1        );pose1         = NULL;
+       if (joint)  { Mem_Free(joint);  joint  = NULL; }
+       if (joint1) { Mem_Free(joint1); joint1 = NULL; }
+       if (pose)   { Mem_Free(pose);   pose   = NULL; }
+       if (pose1)  { Mem_Free(pose1);  pose1  = NULL; }
 }
index 82eca27ce476872558c5c427ff1858d0582439f3..3357d0dfe6968920bd025a6d2afc7f1e7c76f380 100644 (file)
@@ -26,28 +26,21 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include "wad.h"
 
 
+cvar_t r_trippy = {CF_CLIENT, "r_trippy", "0", "easter egg"};
 //cvar_t r_subdivide_size = {CF_CLIENT | CF_ARCHIVE, "r_subdivide_size", "128", "how large water polygons should be (smaller values produce more polygons which give better warping effects)"};
-cvar_t mod_bsp_portalize = {CF_CLIENT | CF_SERVER, "mod_bsp_portalize", "1", "enables portal generation from BSP tree (may take several seconds per map), used by r_drawportals, r_useportalculling, r_shadow_realtime_world_compileportalculling, sv_cullentities_portal"};
 cvar_t r_novis = {CF_CLIENT, "r_novis", "0", "draws whole level, see also sv_cullentities_pvs 0"};
 cvar_t r_nosurftextures = {CF_CLIENT, "r_nosurftextures", "0", "pretends there was no texture lump found in the q1bsp/hlbsp loading (useful for debugging this rare case)"};
+
 cvar_t r_subdivisions_tolerance = {CF_CLIENT, "r_subdivisions_tolerance", "4", "maximum error tolerance on curve subdivision for rendering purposes (in other words, the curves will be given as many polygons as necessary to represent curves at this quality)"};
 cvar_t r_subdivisions_mintess = {CF_CLIENT, "r_subdivisions_mintess", "0", "minimum number of subdivisions (values above 0 will smooth curves that don't need it)"};
 cvar_t r_subdivisions_maxtess = {CF_CLIENT, "r_subdivisions_maxtess", "1024", "maximum number of subdivisions (prevents curves beyond a certain detail level, limits smoothing)"};
 cvar_t r_subdivisions_maxvertices = {CF_CLIENT, "r_subdivisions_maxvertices", "65536", "maximum vertices allowed per subdivided curve"};
-cvar_t r_subdivisions_collision_tolerance = {CF_CLIENT, "r_subdivisions_collision_tolerance", "15", "maximum error tolerance on curve subdivision for collision purposes (usually a larger error tolerance than for rendering)"};
-cvar_t r_subdivisions_collision_mintess = {CF_CLIENT, "r_subdivisions_collision_mintess", "0", "minimum number of subdivisions (values above 0 will smooth curves that don't need it)"};
-cvar_t r_subdivisions_collision_maxtess = {CF_CLIENT, "r_subdivisions_collision_maxtess", "1024", "maximum number of subdivisions (prevents curves beyond a certain detail level, limits smoothing)"};
-cvar_t r_subdivisions_collision_maxvertices = {CF_CLIENT, "r_subdivisions_collision_maxvertices", "4225", "maximum vertices allowed per subdivided curve"};
-cvar_t r_trippy = {CF_CLIENT, "r_trippy", "0", "easter egg"};
-cvar_t r_fxaa = {CF_CLIENT | CF_ARCHIVE, "r_fxaa", "0", "fast approximate anti aliasing"};
-cvar_t mod_noshader_default_offsetmapping = {CF_CLIENT | CF_ARCHIVE, "mod_noshader_default_offsetmapping", "1", "use offsetmapping by default on all surfaces that are not using q3 shader files"};
-cvar_t mod_obj_orientation = {CF_CLIENT | CF_SERVER, "mod_obj_orientation", "1", "fix orientation of OBJ models to the usual conventions (if zero, use coordinates as is)"};
-cvar_t mod_q2bsp_littransparentsurfaces = {CF_CLIENT, "mod_q2bsp_littransparentsurfaces", "0", "allows lighting on rain in 3v3gloom3 and other cases of transparent surfaces that have lightmaps that were ignored by quake2"};
+cvar_t mod_q3bsp_curves_subdivisions_tolerance = {CF_CLIENT | CF_SERVER, "mod_q3bsp_curves_subdivisions_tolerance", "15", "maximum error tolerance on curve subdivision for collision purposes (usually a larger error tolerance than for rendering)"};
+cvar_t mod_q3bsp_curves_subdivisions_mintess = {CF_CLIENT | CF_SERVER, "mod_q3bsp_curves_subdivisions_mintess", "0", "minimum number of subdivisions for collision purposes (values above 0 will smooth curves that don't need it)"};
+cvar_t mod_q3bsp_curves_subdivisions_maxtess = {CF_CLIENT | CF_SERVER, "mod_q3bsp_curves_subdivisions_maxtess", "1024", "maximum number of subdivisions for collision purposes (prevents curves beyond a certain detail level, limits smoothing)"};
+cvar_t mod_q3bsp_curves_subdivisions_maxvertices = {CF_CLIENT | CF_SERVER, "mod_q3bsp_curves_subdivisions_maxvertices", "4225", "maximum vertices allowed per subdivided curve for collision purposes"};
 cvar_t mod_q3bsp_curves_collisions = {CF_CLIENT | CF_SERVER, "mod_q3bsp_curves_collisions", "1", "enables collisions with curves (SLOW)"};
-cvar_t mod_q3bsp_curves_collisions_stride = {CF_CLIENT | CF_SERVER, "mod_q3bsp_curves_collisions_stride", "16", "collisions against curves: optimize performance by doing a combined collision check for this triangle amount first (-1 avoids any box tests)"};
-cvar_t mod_q3bsp_curves_stride = {CF_CLIENT | CF_SERVER, "mod_q3bsp_curves_stride", "16", "particle effect collisions against curves: optimize performance by doing a combined collision check for this triangle amount first (-1 avoids any box tests)"};
 cvar_t mod_q3bsp_optimizedtraceline = {CF_CLIENT | CF_SERVER, "mod_q3bsp_optimizedtraceline", "1", "whether to use optimized traceline code for line traces (as opposed to tracebox code)"};
-cvar_t mod_q3bsp_debugtracebrush = {CF_CLIENT | CF_SERVER, "mod_q3bsp_debugtracebrush", "0", "selects different tracebrush bsp recursion algorithms (for debugging purposes only)"};
 cvar_t mod_q3bsp_lightmapmergepower = {CF_CLIENT | CF_ARCHIVE, "mod_q3bsp_lightmapmergepower", "4", "merges the quake3 128x128 lightmap textures into larger lightmap group textures to speed up rendering, 1 = 256x256, 2 = 512x512, 3 = 1024x1024, 4 = 2048x2048, 5 = 4096x4096, ..."};
 cvar_t mod_q3bsp_nolightmaps = {CF_CLIENT | CF_ARCHIVE, "mod_q3bsp_nolightmaps", "0", "do not load lightmaps in Q3BSP maps (to save video RAM, but be warned: it looks ugly)"};
 cvar_t mod_q3bsp_tracelineofsight_brushes = {CF_CLIENT | CF_SERVER, "mod_q3bsp_tracelineofsight_brushes", "0", "enables culling of entities behind detail brushes, curves, etc"};
@@ -55,6 +48,7 @@ cvar_t mod_q3bsp_sRGBlightmaps = {CF_CLIENT, "mod_q3bsp_sRGBlightmaps", "0", "tr
 cvar_t mod_q3bsp_lightgrid_texture = {CF_CLIENT, "mod_q3bsp_lightgrid_texture", "1", "directly apply the lightgrid as a global texture rather than only reading it at the entity origin"};
 cvar_t mod_q3bsp_lightgrid_world_surfaces = {CF_CLIENT, "mod_q3bsp_lightgrid_world_surfaces", "0", "apply lightgrid lighting to the world bsp geometry rather than using lightmaps (experimental/debug tool)"};
 cvar_t mod_q3bsp_lightgrid_bsp_surfaces = {CF_CLIENT, "mod_q3bsp_lightgrid_bsp_surfaces", "0", "apply lightgrid lighting to bsp models other than the world rather than using their lightmaps (experimental/debug tool)"};
+cvar_t mod_noshader_default_offsetmapping = {CF_CLIENT | CF_ARCHIVE, "mod_noshader_default_offsetmapping", "1", "use offsetmapping by default on all surfaces that are not using q3 shader files"};
 cvar_t mod_q3shader_default_offsetmapping = {CF_CLIENT | CF_ARCHIVE, "mod_q3shader_default_offsetmapping", "1", "use offsetmapping by default on all surfaces that are using q3 shader files"};
 cvar_t mod_q3shader_default_offsetmapping_scale = {CF_CLIENT | CF_ARCHIVE, "mod_q3shader_default_offsetmapping_scale", "1", "default scale used for offsetmapping"};
 cvar_t mod_q3shader_default_offsetmapping_bias = {CF_CLIENT | CF_ARCHIVE, "mod_q3shader_default_offsetmapping_bias", "0", "default bias used for offsetmapping"};
@@ -64,9 +58,16 @@ cvar_t mod_q3shader_default_refractive_index = {CF_CLIENT, "mod_q3shader_default
 cvar_t mod_q3shader_force_addalpha = {CF_CLIENT, "mod_q3shader_force_addalpha", "0", "treat GL_ONE GL_ONE (or add) blendfunc as GL_SRC_ALPHA GL_ONE for compatibility with older DarkPlaces releases"};
 cvar_t mod_q3shader_force_terrain_alphaflag = {CF_CLIENT, "mod_q3shader_force_terrain_alphaflag", "0", "for multilayered terrain shaders force TEXF_ALPHA flag on both layers"};
 
+cvar_t mod_q2bsp_littransparentsurfaces = {CF_CLIENT, "mod_q2bsp_littransparentsurfaces", "0", "allows lighting on rain in 3v3gloom3 and other cases of transparent surfaces that have lightmaps that were ignored by quake2"};
+
 cvar_t mod_q1bsp_polygoncollisions = {CF_CLIENT | CF_SERVER, "mod_q1bsp_polygoncollisions", "0", "disables use of precomputed cliphulls and instead collides with polygons (uses Bounding Interval Hierarchy optimizations)"};
+cvar_t mod_q1bsp_zero_hullsize_cutoff = {CF_CLIENT | CF_SERVER, "mod_q1bsp_zero_hullsize_cutoff", "3", "bboxes with an X dimension smaller than this will use the smallest cliphull (0x0x0) instead of being rounded up to the player cliphull (32x32x56) in Q1BSP, or crouching player (32x32x36) in HLBSP"};
+
+cvar_t mod_bsp_portalize = {CF_CLIENT, "mod_bsp_portalize", "1", "enables portal generation from BSP tree (may take several seconds per map), used by r_drawportals, r_useportalculling, r_shadow_realtime_dlight_portalculling, r_shadow_realtime_world_compileportalculling"};
 cvar_t mod_recalculatenodeboxes = {CF_CLIENT | CF_SERVER, "mod_recalculatenodeboxes", "1", "enables use of generated node bounding boxes based on BSP tree portal reconstruction, rather than the node boxes supplied by the map compiler"};
 
+cvar_t mod_obj_orientation = {CF_CLIENT | CF_SERVER, "mod_obj_orientation", "1", "fix orientation of OBJ models to the usual conventions (if zero, use coordinates as is)"};
+
 static texture_t mod_q1bsp_texture_solid;
 static texture_t mod_q1bsp_texture_sky;
 static texture_t mod_q1bsp_texture_lava;
@@ -85,20 +86,16 @@ void Mod_BrushInit(void)
        Cvar_RegisterVariable(&r_subdivisions_mintess);
        Cvar_RegisterVariable(&r_subdivisions_maxtess);
        Cvar_RegisterVariable(&r_subdivisions_maxvertices);
-       Cvar_RegisterVariable(&r_subdivisions_collision_tolerance);
-       Cvar_RegisterVariable(&r_subdivisions_collision_mintess);
-       Cvar_RegisterVariable(&r_subdivisions_collision_maxtess);
-       Cvar_RegisterVariable(&r_subdivisions_collision_maxvertices);
+       Cvar_RegisterVariable(&mod_q3bsp_curves_subdivisions_tolerance);
+       Cvar_RegisterVariable(&mod_q3bsp_curves_subdivisions_mintess);
+       Cvar_RegisterVariable(&mod_q3bsp_curves_subdivisions_maxtess);
+       Cvar_RegisterVariable(&mod_q3bsp_curves_subdivisions_maxvertices);
        Cvar_RegisterVariable(&r_trippy);
-       Cvar_RegisterVariable(&r_fxaa);
        Cvar_RegisterVariable(&mod_noshader_default_offsetmapping);
        Cvar_RegisterVariable(&mod_obj_orientation);
        Cvar_RegisterVariable(&mod_q2bsp_littransparentsurfaces);
        Cvar_RegisterVariable(&mod_q3bsp_curves_collisions);
-       Cvar_RegisterVariable(&mod_q3bsp_curves_collisions_stride);
-       Cvar_RegisterVariable(&mod_q3bsp_curves_stride);
        Cvar_RegisterVariable(&mod_q3bsp_optimizedtraceline);
-       Cvar_RegisterVariable(&mod_q3bsp_debugtracebrush);
        Cvar_RegisterVariable(&mod_q3bsp_lightmapmergepower);
        Cvar_RegisterVariable(&mod_q3bsp_nolightmaps);
        Cvar_RegisterVariable(&mod_q3bsp_sRGBlightmaps);
@@ -115,6 +112,7 @@ void Mod_BrushInit(void)
        Cvar_RegisterVariable(&mod_q3shader_force_addalpha);
        Cvar_RegisterVariable(&mod_q3shader_force_terrain_alphaflag);
        Cvar_RegisterVariable(&mod_q1bsp_polygoncollisions);
+       Cvar_RegisterVariable(&mod_q1bsp_zero_hullsize_cutoff);
        Cvar_RegisterVariable(&mod_recalculatenodeboxes);
 
        // these games were made for older DP engines and are no longer
@@ -123,27 +121,27 @@ void Mod_BrushInit(void)
                Cvar_SetQuick(&mod_q3shader_force_addalpha, "1");
 
        memset(&mod_q1bsp_texture_solid, 0, sizeof(mod_q1bsp_texture_solid));
-       strlcpy(mod_q1bsp_texture_solid.name, "solid" , sizeof(mod_q1bsp_texture_solid.name));
+       dp_strlcpy(mod_q1bsp_texture_solid.name, "solid" , sizeof(mod_q1bsp_texture_solid.name));
        mod_q1bsp_texture_solid.surfaceflags = 0;
        mod_q1bsp_texture_solid.supercontents = SUPERCONTENTS_SOLID;
 
        mod_q1bsp_texture_sky = mod_q1bsp_texture_solid;
-       strlcpy(mod_q1bsp_texture_sky.name, "sky", sizeof(mod_q1bsp_texture_sky.name));
+       dp_strlcpy(mod_q1bsp_texture_sky.name, "sky", sizeof(mod_q1bsp_texture_sky.name));
        mod_q1bsp_texture_sky.surfaceflags = Q3SURFACEFLAG_SKY | Q3SURFACEFLAG_NOIMPACT | Q3SURFACEFLAG_NOMARKS | Q3SURFACEFLAG_NODLIGHT | Q3SURFACEFLAG_NOLIGHTMAP;
        mod_q1bsp_texture_sky.supercontents = SUPERCONTENTS_SKY | SUPERCONTENTS_NODROP;
 
        mod_q1bsp_texture_lava = mod_q1bsp_texture_solid;
-       strlcpy(mod_q1bsp_texture_lava.name, "*lava", sizeof(mod_q1bsp_texture_lava.name));
+       dp_strlcpy(mod_q1bsp_texture_lava.name, "*lava", sizeof(mod_q1bsp_texture_lava.name));
        mod_q1bsp_texture_lava.surfaceflags = Q3SURFACEFLAG_NOMARKS;
        mod_q1bsp_texture_lava.supercontents = SUPERCONTENTS_LAVA | SUPERCONTENTS_NODROP;
 
        mod_q1bsp_texture_slime = mod_q1bsp_texture_solid;
-       strlcpy(mod_q1bsp_texture_slime.name, "*slime", sizeof(mod_q1bsp_texture_slime.name));
+       dp_strlcpy(mod_q1bsp_texture_slime.name, "*slime", sizeof(mod_q1bsp_texture_slime.name));
        mod_q1bsp_texture_slime.surfaceflags = Q3SURFACEFLAG_NOMARKS;
        mod_q1bsp_texture_slime.supercontents = SUPERCONTENTS_SLIME;
 
        mod_q1bsp_texture_water = mod_q1bsp_texture_solid;
-       strlcpy(mod_q1bsp_texture_water.name, "*water", sizeof(mod_q1bsp_texture_water.name));
+       dp_strlcpy(mod_q1bsp_texture_water.name, "*water", sizeof(mod_q1bsp_texture_water.name));
        mod_q1bsp_texture_water.surfaceflags = Q3SURFACEFLAG_NOMARKS;
        mod_q1bsp_texture_water.supercontents = SUPERCONTENTS_WATER;
 }
@@ -762,7 +760,7 @@ static int Mod_Q1BSP_RecursiveHullCheck(RecursiveHullCheckTraceInfo_t *t, int nu
                        // recurse both sides, front side first
                        ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[p1side], p1f, midf, p1, mid);
                        // if this side is not empty, return what it is (solid or done)
-                       if (ret != HULLCHECKSTATE_EMPTY)
+                       if (ret != HULLCHECKSTATE_EMPTY && !t->trace->allsolid)
                                return ret;
 
                        ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[p2side], midf, p2f, mid, p2);
@@ -976,7 +974,7 @@ static void Mod_Q1BSP_TraceBox(struct model_s *model, const frameblend_t *frameb
        rhc.trace->fraction = 1;
        rhc.trace->allsolid = true;
        VectorSubtract(boxmaxs, boxmins, boxsize);
-       if (boxsize[0] < 3)
+       if (boxsize[0] < mod_q1bsp_zero_hullsize_cutoff.value)
                rhc.hull = &model->brushq1.hulls[0]; // 0x0x0
        else if (model->brush.ishlbsp)
        {
@@ -1005,7 +1003,7 @@ static void Mod_Q1BSP_TraceBox(struct model_s *model, const frameblend_t *frameb
        VectorMAMAM(1, end, 1, boxmins, -1, rhc.hull->clip_mins, rhc.end);
        VectorSubtract(rhc.end, rhc.start, rhc.dist);
 #if COLLISIONPARANOID >= 2
-       Con_Printf("t(%f %f %f,%f %f %f,%i %f %f %f)", rhc.start[0], rhc.start[1], rhc.start[2], rhc.end[0], rhc.end[1], rhc.end[2], rhc.hull - model->brushq1.hulls, rhc.hull->clip_mins[0], rhc.hull->clip_mins[1], rhc.hull->clip_mins[2]);
+       Con_Printf("t(%f %f %f,%f %f %f,%li %f %f %f)", rhc.start[0], rhc.start[1], rhc.start[2], rhc.end[0], rhc.end[1], rhc.end[2], rhc.hull - model->brushq1.hulls, rhc.hull->clip_mins[0], rhc.hull->clip_mins[1], rhc.hull->clip_mins[2]);
        Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
        {
 
@@ -1685,6 +1683,10 @@ static void Mod_Q1BSP_LoadTextures(sizebuf_t *sb)
                        // pretty up the buffer (replacing any trailing garbage with 0)
                        for (j = (int)strlen(name); j < 16; j++)
                                name[j] = 0;
+                       // bones_was_here: force all names to lowercase (matching code below) so we don't crash on e2m9
+                       for (j = 0;name[j];j++)
+                               if (name[j] >= 'A' && name[j] <= 'Z')
+                                       name[j] += 'a' - 'A';
 
                        if (!strncmp(name, "sky", 3))
                                numsky++;
@@ -1712,7 +1714,7 @@ static void Mod_Q1BSP_LoadTextures(sizebuf_t *sb)
        skinframemissing = R_SkinFrame_LoadMissing();
        for (i = 0, tx = loadmodel->data_textures;i < loadmodel->num_textures;i++, tx++)
        {
-               strlcpy(tx->name, "NO TEXTURE FOUND", sizeof(tx->name));
+               dp_strlcpy(tx->name, "NO TEXTURE FOUND", sizeof(tx->name));
                tx->width = 16;
                tx->height = 16;
                tx->basealpha = 1.0f;
@@ -1830,7 +1832,7 @@ static void Mod_Q1BSP_LoadTextures(sizebuf_t *sb)
                // no luck with loading shaders or external textures - restore the in-progress texture loading
                loadmodel->data_textures[i] = backuptex;
 
-               strlcpy(tx->name, name, sizeof(tx->name));
+               dp_strlcpy(tx->name, name, sizeof(tx->name));
                tx->width = mtwidth;
                tx->height = mtheight;
                tx->basealpha = 1.0f;
@@ -2107,11 +2109,11 @@ static void Mod_Q1BSP_LoadLighting(sizebuf_t *sb)
        else // LadyHavoc: bsp version 29 (normal white lighting)
        {
                // LadyHavoc: hope is not lost yet, check for a .lit file to load
-               strlcpy (litfilename, loadmodel->name, sizeof (litfilename));
+               dp_strlcpy (litfilename, loadmodel->name, sizeof (litfilename));
                FS_StripExtension (litfilename, litfilename, sizeof (litfilename));
-               strlcpy (dlitfilename, litfilename, sizeof (dlitfilename));
-               strlcat (litfilename, ".lit", sizeof (litfilename));
-               strlcat (dlitfilename, ".dlit", sizeof (dlitfilename));
+               dp_strlcpy (dlitfilename, litfilename, sizeof (dlitfilename));
+               dp_strlcat (litfilename, ".lit", sizeof (litfilename));
+               dp_strlcat (dlitfilename, ".dlit", sizeof (dlitfilename));
                data = (unsigned char*) FS_LoadFile(litfilename, tempmempool, false, &filesize);
                if (data)
                {
@@ -2204,9 +2206,9 @@ static void Mod_Q1BSP_ParseWadsFromEntityLump(const char *data)
                if (com_token[0] == '}')
                        break; // end of worldspawn
                if (com_token[0] == '_')
-                       strlcpy(key, com_token + 1, sizeof(key));
+                       dp_strlcpy(key, com_token + 1, sizeof(key));
                else
-                       strlcpy(key, com_token, sizeof(key));
+                       dp_strlcpy(key, com_token, sizeof(key));
                while (key[strlen(key)-1] == ' ') // remove trailing spaces
                        key[strlen(key)-1] = 0;
                if (!COM_ParseToken_Simple(&data, false, false, true))
@@ -3826,11 +3828,11 @@ static void Mod_Q1BSP_RoundUpToHullSize(model_t *cmodel, const vec3_t inmins, co
        const hull_t *hull;
 
        VectorSubtract(inmaxs, inmins, size);
-       if (cmodel->brush.ishlbsp)
+       if (size[0] < mod_q1bsp_zero_hullsize_cutoff.value)
+               hull = &cmodel->brushq1.hulls[0]; // 0x0x0
+       else if (cmodel->brush.ishlbsp)
        {
-               if (size[0] < 3)
-                       hull = &cmodel->brushq1.hulls[0]; // 0x0x0
-               else if (size[0] <= 32)
+               if (size[0] <= 32)
                {
                        if (size[2] < 54) // pick the nearest of 36 or 72
                                hull = &cmodel->brushq1.hulls[3]; // 32x32x36
@@ -3842,9 +3844,7 @@ static void Mod_Q1BSP_RoundUpToHullSize(model_t *cmodel, const vec3_t inmins, co
        }
        else
        {
-               if (size[0] < 3)
-                       hull = &cmodel->brushq1.hulls[0]; // 0x0x0
-               else if (size[0] <= 32)
+               if (size[0] <= 32)
                        hull = &cmodel->brushq1.hulls[1]; // 32x32x56
                else
                        hull = &cmodel->brushq1.hulls[2]; // 64x64x88
@@ -3887,7 +3887,8 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer, void *bufferend)
        hullinfo_t hullinfo;
        int totalstylesurfaces, totalstyles, stylecounts[256], remapstyles[256];
        model_brush_lightstyleinfo_t styleinfo[256];
-       unsigned char *datapointer;
+       int *datapointer;
+       model_brush_lightstyleinfo_t *lsidatapointer;
        sizebuf_t sb;
 
        MSG_InitReadBuffer(&sb, (unsigned char *)buffer, (unsigned char *)bufferend - (unsigned char *)buffer);
@@ -4008,7 +4009,7 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer, void *bufferend)
        mod->brushq1.num_compressedpvs = 0;
 
        Mod_Q1BSP_MakeHull0();
-       if (mod_bsp_portalize.integer)
+       if (mod_bsp_portalize.integer && cls.state != ca_dedicated)
                Mod_BSP_MakePortals();
 
        mod->numframes = 2;             // regular and alternate animation
@@ -4049,8 +4050,11 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer, void *bufferend)
                                totalstylesurfaces += stylecounts[k];
                }
        }
-       datapointer = (unsigned char *)Mem_Alloc(mod->mempool, mod->num_surfaces * sizeof(int) + totalstyles * sizeof(model_brush_lightstyleinfo_t) + totalstylesurfaces * sizeof(int *));
-       mod->modelsurfaces_sorted = (int*)datapointer;datapointer += mod->num_surfaces * sizeof(int);
+       // bones_was_here: using a separate allocation for model_brush_lightstyleinfo_t
+       // because on a 64-bit machine it no longer has the same alignment requirement as int.
+       lsidatapointer = Mem_AllocType(mod->mempool, model_brush_lightstyleinfo_t, totalstyles * sizeof(model_brush_lightstyleinfo_t));
+       datapointer = Mem_AllocType(mod->mempool, int, mod->num_surfaces * sizeof(int) + totalstylesurfaces * sizeof(int));
+       mod->modelsurfaces_sorted = datapointer;datapointer += mod->num_surfaces;
        for (i = 0;i < mod->brush.numsubmodels;i++)
        {
                // LadyHavoc: this code was originally at the end of this loop, but
@@ -4065,7 +4069,7 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer, void *bufferend)
                        // copy the base model to this one
                        *mod = *loadmodel;
                        // rename the clone back to its proper name
-                       strlcpy(mod->name, name, sizeof(mod->name));
+                       dp_strlcpy(mod->name, name, sizeof(mod->name));
                        mod->brush.parentmodel = loadmodel;
                        // textures and memory belong to the main model
                        mod->texturepool = NULL;
@@ -4142,7 +4146,7 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer, void *bufferend)
                                        styleinfo[mod->brushq1.num_lightstyles].style = k;
                                        styleinfo[mod->brushq1.num_lightstyles].value = 0;
                                        styleinfo[mod->brushq1.num_lightstyles].numsurfaces = 0;
-                                       styleinfo[mod->brushq1.num_lightstyles].surfacelist = (int *)datapointer;datapointer += stylecounts[k] * sizeof(int);
+                                       styleinfo[mod->brushq1.num_lightstyles].surfacelist = datapointer;datapointer += stylecounts[k];
                                        remapstyles[k] = mod->brushq1.num_lightstyles;
                                        mod->brushq1.num_lightstyles++;
                                }
@@ -4159,7 +4163,7 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer, void *bufferend)
                                        }
                                }
                        }
-                       mod->brushq1.data_lightstyleinfo = (model_brush_lightstyleinfo_t *)datapointer;datapointer += mod->brushq1.num_lightstyles * sizeof(model_brush_lightstyleinfo_t);
+                       mod->brushq1.data_lightstyleinfo = lsidatapointer;lsidatapointer += mod->brushq1.num_lightstyles;
                        memcpy(mod->brushq1.data_lightstyleinfo, styleinfo, mod->brushq1.num_lightstyles * sizeof(model_brush_lightstyleinfo_t));
                }
                else
@@ -4824,7 +4828,8 @@ static void Mod_Q2BSP_Load(model_t *mod, void *buffer, void *bufferend)
        msurface_t *surface;
        int totalstylesurfaces, totalstyles, stylecounts[256], remapstyles[256];
        model_brush_lightstyleinfo_t styleinfo[256];
-       unsigned char *datapointer;
+       int *datapointer;
+       model_brush_lightstyleinfo_t *lsidatapointer;
        sizebuf_t sb;
 
        MSG_InitReadBuffer(&sb, (unsigned char *)buffer, (unsigned char *)bufferend - (unsigned char *)buffer);
@@ -4939,7 +4944,7 @@ static void Mod_Q2BSP_Load(model_t *mod, void *buffer, void *bufferend)
        mod->brushq1.num_compressedpvs = 0;
 
        // the MakePortals code works fine on the q2bsp data as well
-       if (mod_bsp_portalize.integer)
+       if (mod_bsp_portalize.integer && cls.state != ca_dedicated)
                Mod_BSP_MakePortals();
 
        mod->numframes = 0;             // q2bsp animations are kind of special, frame is unbounded...
@@ -4966,8 +4971,11 @@ static void Mod_Q2BSP_Load(model_t *mod, void *buffer, void *bufferend)
                                totalstylesurfaces += stylecounts[k];
                }
        }
-       datapointer = (unsigned char *)Mem_Alloc(mod->mempool, mod->num_surfaces * sizeof(int) + totalstyles * sizeof(model_brush_lightstyleinfo_t) + totalstylesurfaces * sizeof(int *));
-       mod->modelsurfaces_sorted = (int*)datapointer; datapointer += mod->num_surfaces * sizeof(int);
+       // bones_was_here: using a separate allocation for model_brush_lightstyleinfo_t
+       // because on a 64-bit machine it no longer has the same alignment requirement as int.
+       lsidatapointer = Mem_AllocType(mod->mempool, model_brush_lightstyleinfo_t, totalstyles * sizeof(model_brush_lightstyleinfo_t));
+       datapointer = Mem_AllocType(mod->mempool, int, mod->num_surfaces * sizeof(int) + totalstylesurfaces * sizeof(int));
+       mod->modelsurfaces_sorted = datapointer; datapointer += mod->num_surfaces;
        // set up the world model, then on each submodel copy from the world model
        // and set up the submodel with the respective model info.
        mod = loadmodel;
@@ -4984,7 +4992,7 @@ static void Mod_Q2BSP_Load(model_t *mod, void *buffer, void *bufferend)
                        // copy the base model to this one
                        *mod = *loadmodel;
                        // rename the clone back to its proper name
-                       strlcpy(mod->name, name, sizeof(mod->name));
+                       dp_strlcpy(mod->name, name, sizeof(mod->name));
                        mod->brush.parentmodel = loadmodel;
                        // textures and memory belong to the main model
                        mod->texturepool = NULL;
@@ -5070,7 +5078,7 @@ static void Mod_Q2BSP_Load(model_t *mod, void *buffer, void *bufferend)
                                        styleinfo[mod->brushq1.num_lightstyles].style = k;
                                        styleinfo[mod->brushq1.num_lightstyles].value = 0;
                                        styleinfo[mod->brushq1.num_lightstyles].numsurfaces = 0;
-                                       styleinfo[mod->brushq1.num_lightstyles].surfacelist = (int *)datapointer;datapointer += stylecounts[k] * sizeof(int);
+                                       styleinfo[mod->brushq1.num_lightstyles].surfacelist = datapointer;datapointer += stylecounts[k];
                                        remapstyles[k] = mod->brushq1.num_lightstyles;
                                        mod->brushq1.num_lightstyles++;
                                }
@@ -5087,7 +5095,7 @@ static void Mod_Q2BSP_Load(model_t *mod, void *buffer, void *bufferend)
                                        }
                                }
                        }
-                       mod->brushq1.data_lightstyleinfo = (model_brush_lightstyleinfo_t *)datapointer;datapointer += mod->brushq1.num_lightstyles * sizeof(model_brush_lightstyleinfo_t);
+                       mod->brushq1.data_lightstyleinfo = lsidatapointer;lsidatapointer += mod->brushq1.num_lightstyles;
                        memcpy(mod->brushq1.data_lightstyleinfo, styleinfo, mod->brushq1.num_lightstyles * sizeof(model_brush_lightstyleinfo_t));
                }
                else
@@ -5143,14 +5151,14 @@ static void Mod_Q3BSP_LoadEntities(lump_t *l)
                        if (com_token[0] == '}')
                                break; // end of worldspawn
                        if (com_token[0] == '_')
-                               strlcpy(key, com_token + 1, sizeof(key));
+                               dp_strlcpy(key, com_token + 1, sizeof(key));
                        else
-                               strlcpy(key, com_token, sizeof(key));
+                               dp_strlcpy(key, com_token, sizeof(key));
                        while (key[strlen(key)-1] == ' ') // remove trailing spaces
                                key[strlen(key)-1] = 0;
                        if (!COM_ParseToken_Simple(&data, false, false, true))
                                break; // error
-                       strlcpy(value, com_token, sizeof(value));
+                       dp_strlcpy(value, com_token, sizeof(value));
                        if (!strcasecmp("gridsize", key)) // this one is case insensitive to 100% match q3map2
                        {
 #if _MSC_VER >= 1400
@@ -5368,7 +5376,7 @@ static void Mod_Q3BSP_LoadEffects(lump_t *l)
 
        for (i = 0;i < count;i++, in++, out++)
        {
-               strlcpy (out->shadername, in->shadername, sizeof (out->shadername));
+               dp_strlcpy (out->shadername, in->shadername, sizeof (out->shadername));
                n = LittleLong(in->brushindex);
                if (n >= loadmodel->brush.num_brushes)
                {
@@ -5893,7 +5901,7 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l)
                case Q3FACETYPE_PATCH:
                        patchsize[0] = LittleLong(in->specific.patch.patchsize[0]);
                        patchsize[1] = LittleLong(in->specific.patch.patchsize[1]);
-                       if (numvertices != (patchsize[0] * patchsize[1]) || patchsize[0] < 3 || patchsize[1] < 3 || !(patchsize[0] & 1) || !(patchsize[1] & 1) || patchsize[0] * patchsize[1] >= min(r_subdivisions_maxvertices.integer, r_subdivisions_collision_maxvertices.integer))
+                       if (numvertices != (patchsize[0] * patchsize[1]) || patchsize[0] < 3 || patchsize[1] < 3 || !(patchsize[0] & 1) || !(patchsize[1] & 1) || patchsize[0] * patchsize[1] >= (cls.state == ca_dedicated ? mod_q3bsp_curves_subdivisions_maxvertices.integer : min(r_subdivisions_maxvertices.integer, mod_q3bsp_curves_subdivisions_maxvertices.integer)))
                        {
                                Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid patchsize %ix%i\n", i, out->texture->name, patchsize[0], patchsize[1]);
                                continue;
@@ -5912,11 +5920,11 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l)
 
                        // lower quality collision patches! Same procedure as before, but different cvars
                        // convert patch to Q3FACETYPE_MESH
-                       cxtess = Q3PatchTesselationOnX(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_collision_tolerance.value);
-                       cytess = Q3PatchTesselationOnY(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_collision_tolerance.value);
+                       cxtess = Q3PatchTesselationOnX(patchsize[0], patchsize[1], 3, originalvertex3f, mod_q3bsp_curves_subdivisions_tolerance.value);
+                       cytess = Q3PatchTesselationOnY(patchsize[0], patchsize[1], 3, originalvertex3f, mod_q3bsp_curves_subdivisions_tolerance.value);
                        // bound to user settings
-                       cxtess = bound(r_subdivisions_collision_mintess.integer, cxtess, r_subdivisions_collision_maxtess.integer);
-                       cytess = bound(r_subdivisions_collision_mintess.integer, cytess, r_subdivisions_collision_maxtess.integer);
+                       cxtess = bound(mod_q3bsp_curves_subdivisions_mintess.integer, cxtess, mod_q3bsp_curves_subdivisions_maxtess.integer);
+                       cytess = bound(mod_q3bsp_curves_subdivisions_mintess.integer, cytess, mod_q3bsp_curves_subdivisions_maxtess.integer);
                        // bound to sanity settings
                        cxtess = bound(0, cxtess, 1024);
                        cytess = bound(0, cytess, 1024);
@@ -6563,7 +6571,7 @@ static void Mod_Q3BSP_LoadLightGrid(lump_t *l)
                        lightgridmatrix[1][3] = -(loadmodel->brushq3.num_lightgrid_imins[1] - 0.5f) / texturesize[1];
                        lightgridmatrix[2][3] = -(loadmodel->brushq3.num_lightgrid_imins[2] - 1.5f) / texturesize[2];
                        lightgridmatrix[3][3] = 1;
-                       Matrix4x4_FromArrayDoubleD3D(&loadmodel->brushq3.lightgridworldtotexturematrix, lightgridmatrix[0]);
+                       Matrix4x4_FromArrayDoubleD3D(&loadmodel->brushq3.lightgridworldtotexturematrix, lightgridmatrix);
                        loadmodel->brushq3.lightgridtexture = R_LoadTexture3D(loadmodel->texturepool, "lightgrid", texturesize[0], texturesize[1], texturesize[2], texturergba, TEXTYPE_RGBA, TEXF_CLAMP, 0, NULL);
                        Mem_Free(texturergba);
                }
@@ -7520,7 +7528,7 @@ static void Mod_Q3BSP_Load(model_t *mod, void *buffer, void *bufferend)
        loadmodel->brush.numsubmodels = loadmodel->brushq3.num_models;
 
        // the MakePortals code works fine on the q3bsp data as well
-       if (mod_bsp_portalize.integer)
+       if (mod_bsp_portalize.integer && cls.state != ca_dedicated)
                Mod_BSP_MakePortals();
 
        // FIXME: shader alpha should replace r_wateralpha support in q3bsp
@@ -7545,7 +7553,7 @@ static void Mod_Q3BSP_Load(model_t *mod, void *buffer, void *bufferend)
                        // copy the base model to this one
                        *mod = *loadmodel;
                        // rename the clone back to its proper name
-                       strlcpy(mod->name, name, sizeof(mod->name));
+                       dp_strlcpy(mod->name, name, sizeof(mod->name));
                        mod->brush.parentmodel = loadmodel;
                        // textures and memory belong to the main model
                        mod->texturepool = NULL;
@@ -8390,7 +8398,7 @@ void Mod_OBJ_Load(model_t *mod, void *buffer, void *bufferend)
                                        texturenames = (char *)Mem_Realloc(loadmodel->mempool, texturenames, maxtextures * MAX_QPATH);
                                }
                                textureindex = numtextures++;
-                               strlcpy(texturenames + textureindex*MAX_QPATH, loadmodel->name, MAX_QPATH);
+                               dp_strlcpy(texturenames + textureindex*MAX_QPATH, loadmodel->name, MAX_QPATH);
                        }
                        for (j = 1;j < argc;j++)
                        {
@@ -8483,7 +8491,7 @@ void Mod_OBJ_Load(model_t *mod, void *buffer, void *bufferend)
                                        texturenames = (char *)Mem_Realloc(loadmodel->mempool, texturenames, maxtextures * MAX_QPATH);
                                }
                                textureindex = numtextures++;
-                               strlcpy(texturenames + textureindex*MAX_QPATH, argv[1], MAX_QPATH);
+                               dp_strlcpy(texturenames + textureindex*MAX_QPATH, argv[1], MAX_QPATH);
                        }
                }
        }
@@ -8693,7 +8701,7 @@ void Mod_OBJ_Load(model_t *mod, void *buffer, void *bufferend)
                        // copy the base model to this one
                        *mod = *loadmodel;
                        // rename the clone back to its proper name
-                       strlcpy(mod->name, name, sizeof(mod->name));
+                       dp_strlcpy(mod->name, name, sizeof(mod->name));
                        mod->brush.parentmodel = loadmodel;
                        // textures and memory belong to the main model
                        mod->texturepool = NULL;
index 8e707337afe4fe8c9b8d4273ddae4247272112c5..d6cea915396455ffce1b181e5ed6a0f99018af31 100644 (file)
@@ -217,7 +217,7 @@ void Mod_UnloadModel (model_t *mod)
        if (developer_loading.integer)
                Con_Printf("unloading model %s\n", mod->name);
 
-       strlcpy(name, mod->name, sizeof(name));
+       dp_strlcpy(name, mod->name, sizeof(name));
        parentmodel = mod->brush.parentmodel;
        used = mod->used;
        if (mod->mempool)
@@ -246,7 +246,7 @@ void Mod_UnloadModel (model_t *mod)
        // clear the struct to make it available
        memset(mod, 0, sizeof(model_t));
        // restore the fields we want to preserve
-       strlcpy(mod->name, name, sizeof(mod->name));
+       dp_strlcpy(mod->name, name, sizeof(mod->name));
        mod->brush.parentmodel = parentmodel;
        mod->used = used;
        mod->loaded = false;
@@ -315,7 +315,7 @@ static int Mod_FrameGroupify_ParseGroups(const char *buf, mod_framegroupify_pars
                name[0] = 0;
                if (bufptr && strcmp(com_token, "\n"))
                {
-                       strlcpy(name, com_token, sizeof(name));
+                       dp_strlcpy(name, com_token, sizeof(name));
                        COM_ParseToken_Simple(&bufptr, true, false, true);
                }
 
@@ -338,7 +338,7 @@ static void Mod_FrameGroupify_ParseGroups_Store (unsigned int i, int start, int
        model_t *mod = (model_t *) pass;
        animscene_t *anim = &mod->animscenes[i];
        if(name)
-               strlcpy(anim->name, name, sizeof(anim[i].name));
+               dp_strlcpy(anim->name, name, sizeof(anim[i].name));
        else
                dpsnprintf(anim->name, sizeof(anim[i].name), "groupified_%d_anim", i);
        anim->firstframe = bound(0, start, mod->num_poses - 1);
@@ -618,7 +618,7 @@ model_t *Mod_FindName(const char *name, const char *parentname)
 
        // no match found, create a new one
        mod = (model_t *) Mem_ExpandableArray_AllocRecord(&models);
-       strlcpy(mod->name, name, sizeof(mod->name));
+       dp_strlcpy(mod->name, name, sizeof(mod->name));
        if (parentname[0])
                mod->brush.parentmodel = Mod_FindName(parentname, NULL);
        else
@@ -1044,6 +1044,12 @@ void Mod_ShadowMesh_AddMesh(shadowmesh_t *mesh, const float *vertex3f, int numtr
 
        for (i = 0;i < numtris;i++)
        {
+               if ((mesh->numtriangles * 3 + 2) * sizeof(int) + 1 >= ((memheader_t *)((unsigned char *)mesh->element3i - sizeof(memheader_t)))->size)
+               {
+                       // FIXME: we didn't allocate enough space for all the tris, see R_Mod_CompileShadowMap
+                       Con_Print(CON_WARN "Mod_ShadowMesh_AddMesh: insufficient memory allocated!\n");
+                       return;
+               }
                mesh->element3i[mesh->numtriangles * 3 + 0] = Mod_ShadowMesh_AddVertex(mesh, vertex3f + 3 * element3i[i * 3 + 0]);
                mesh->element3i[mesh->numtriangles * 3 + 1] = Mod_ShadowMesh_AddVertex(mesh, vertex3f + 3 * element3i[i * 3 + 1]);
                mesh->element3i[mesh->numtriangles * 3 + 2] = Mod_ShadowMesh_AddVertex(mesh, vertex3f + 3 * element3i[i * 3 + 2]);
@@ -1512,7 +1518,7 @@ void Mod_LoadQ3Shaders(void)
                                        // name
                                        j = (int)strlen(com_token)+1;
                                        custsurfaceparmnames[numcustsurfaceflags] = (char *)Mem_Alloc(tempmempool, j);
-                                       strlcpy(custsurfaceparmnames[numcustsurfaceflags], com_token, j+1);
+                                       dp_strlcpy(custsurfaceparmnames[numcustsurfaceflags], com_token, j+1);
                                        // value
                                        if (COM_ParseToken_QuakeC(&text, false))
                                                custsurfaceflags[numcustsurfaceflags] = strtol(com_token, NULL, 0);
@@ -1574,7 +1580,7 @@ void Mod_LoadQ3Shaders(void)
                        // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
                        // JUST GREP FOR "specularscalemod = 1".
 
-                       strlcpy(shader.name, com_token, sizeof(shader.name));
+                       dp_strlcpy(shader.name, com_token, sizeof(shader.name));
                        if (!COM_ParseToken_QuakeC(&text, false) || strcasecmp(com_token, "{"))
                        {
                                Con_DPrintf("%s parsing error - expected \"{\", found \"%s\"\n", search->filenames[fileindex], com_token);
@@ -1617,7 +1623,7 @@ void Mod_LoadQ3Shaders(void)
                                                                if(j == 0 && !strncasecmp(com_token, "dp_", 3))
                                                                        dpsnprintf(parameter[j], sizeof(parameter[j]), "dp%s", &com_token[3]);
                                                                else
-                                                                       strlcpy(parameter[j], com_token, sizeof(parameter[j]));
+                                                                       dp_strlcpy(parameter[j], com_token, sizeof(parameter[j]));
                                                                numparameters = j + 1;
                                                        }
                                                        if (!COM_ParseToken_QuakeC(&text, true))
@@ -1872,7 +1878,7 @@ void Mod_LoadQ3Shaders(void)
                                                if(j == 0 && !strncasecmp(com_token, "dp_", 3))
                                                        dpsnprintf(parameter[j], sizeof(parameter[j]), "dp%s", &com_token[3]);
                                                else
-                                                       strlcpy(parameter[j], com_token, sizeof(parameter[j]));
+                                                       dp_strlcpy(parameter[j], com_token, sizeof(parameter[j]));
                                                numparameters = j + 1;
                                        }
                                        if (!COM_ParseToken_QuakeC(&text, true))
@@ -1982,7 +1988,7 @@ void Mod_LoadQ3Shaders(void)
                                else if (!strcasecmp(parameter[0], "dpnortlight"))
                                        shader.dpnortlight = true;
                                else if (!strcasecmp(parameter[0], "dpreflectcube"))
-                                       strlcpy(shader.dpreflectcube, parameter[1], sizeof(shader.dpreflectcube));
+                                       dp_strlcpy(shader.dpreflectcube, parameter[1], sizeof(shader.dpreflectcube));
                                else if (!strcasecmp(parameter[0], "dpmeshcollisions"))
                                        shader.dpmeshcollisions = true;
                                // this sets dpshaderkill to true if dpshaderkillifcvarzero was used, and to false if dpnoshaderkillifcvarzero was used
@@ -2041,14 +2047,14 @@ void Mod_LoadQ3Shaders(void)
                                {
                                        // some q3 skies don't have the sky parm set
                                        shader.surfaceparms |= Q3SURFACEPARM_SKY;
-                                       strlcpy(shader.skyboxname, parameter[1], sizeof(shader.skyboxname));
+                                       dp_strlcpy(shader.skyboxname, parameter[1], sizeof(shader.skyboxname));
                                }
                                else if (!strcasecmp(parameter[0], "skyparms") && numparameters >= 2)
                                {
                                        // some q3 skies don't have the sky parm set
                                        shader.surfaceparms |= Q3SURFACEPARM_SKY;
                                        if (!atoi(parameter[1]) && strcasecmp(parameter[1], "-"))
-                                               strlcpy(shader.skyboxname, parameter[1], sizeof(shader.skyboxname));
+                                               dp_strlcpy(shader.skyboxname, parameter[1], sizeof(shader.skyboxname));
                                }
                                else if (!strcasecmp(parameter[0], "cull") && numparameters >= 2)
                                {
@@ -2268,7 +2274,7 @@ qbool Mod_LoadTextureFromQ3Shader(mempool_t *mempool, const char *modelname, tex
        shader_t *shader;
        if (!name)
                name = "";
-       strlcpy(texture->name, name, sizeof(texture->name));
+       dp_strlcpy(texture->name, name, sizeof(texture->name));
        texture->basealpha = 1.0f;
        shader = name[0] ? Mod_LookupQ3Shader(name) : NULL;
 
@@ -2649,7 +2655,7 @@ void Mod_LoadCustomMaterial(mempool_t *mempool, texture_t *texture, const char *
        if (!(materialflags & (MATERIALFLAG_WALL | MATERIALFLAG_SKY)))
                Con_DPrintf("^1Custom texture ^3\"%s\" does not have MATERIALFLAG_WALL set\n", texture->name);
 
-       strlcpy(texture->name, name, sizeof(texture->name));
+       dp_strlcpy(texture->name, name, sizeof(texture->name));
        texture->basealpha = 1.0f;
        texture->basematerialflags = materialflags;
        texture->supercontents = supercontents;
@@ -2749,7 +2755,7 @@ tag_torso,
                        do
                        {
                                if (words < 10)
-                                       strlcpy(word[words++], com_token, sizeof (word[0]));
+                                       dp_strlcpy(word[words++], com_token, sizeof (word[0]));
                                else
                                        wordsoverflow = true;
                        }
@@ -2769,8 +2775,8 @@ tag_torso,
                                        skinfileitem = (skinfileitem_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfileitem_t));
                                        skinfileitem->next = skinfile->items;
                                        skinfile->items = skinfileitem;
-                                       strlcpy (skinfileitem->name, word[1], sizeof (skinfileitem->name));
-                                       strlcpy (skinfileitem->replacement, word[2], sizeof (skinfileitem->replacement));
+                                       dp_strlcpy (skinfileitem->name, word[1], sizeof (skinfileitem->name));
+                                       dp_strlcpy (skinfileitem->replacement, word[2], sizeof (skinfileitem->replacement));
                                }
                                else
                                        Con_Printf("Mod_LoadSkinFiles: parsing error in file \"%s_%i.skin\" on line #%i: wrong number of parameters to command \"%s\", see documentation in DP_GFX_SKINFILES extension in dpextensions.qc\n", loadmodel->name, i, line, word[0]);
@@ -2788,8 +2794,8 @@ tag_torso,
                                skinfileitem = (skinfileitem_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfileitem_t));
                                skinfileitem->next = skinfile->items;
                                skinfile->items = skinfileitem;
-                               strlcpy (skinfileitem->name, word[0], sizeof (skinfileitem->name));
-                               strlcpy (skinfileitem->replacement, word[2], sizeof (skinfileitem->replacement));
+                               dp_strlcpy (skinfileitem->name, word[0], sizeof (skinfileitem->name));
+                               dp_strlcpy (skinfileitem->replacement, word[2], sizeof (skinfileitem->replacement));
                        }
                        else
                                Con_Printf("Mod_LoadSkinFiles: parsing error in file \"%s_%i.skin\" on line #%i: does not look like tag or mesh specification, or replace command, see documentation in DP_GFX_SKINFILES extension in dpextensions.qc\n", loadmodel->name, i, line);
@@ -3058,7 +3064,7 @@ static void Mod_Decompile_OBJ(model_t *model, const char *filename, const char *
                if (textureindex >= maxtextures)
                        continue; // just a precaution
                textureindex = counttextures++;
-               strlcpy(texturenames + textureindex * MAX_QPATH, texname, MAX_QPATH);
+               dp_strlcpy(texturenames + textureindex * MAX_QPATH, texname, MAX_QPATH);
                if (outbufferpos >= outbuffermax >> 1)
                {
                        outbuffermax *= 2;
@@ -3203,7 +3209,7 @@ static void Mod_Decompile_SMD(model_t *model, const char *filename, int firstpos
                        // strangely the smd angles are for a transposed matrix, so we
                        // have to generate a transposed matrix, then convert that...
                        Matrix4x4_FromBonePose7s(&posematrix, model->num_posescale, model->data_poses7s + 7*(model->num_bones * poseindex + transformindex));
-                       Matrix4x4_ToArray12FloatGL(&posematrix, mtest[0]);
+                       Matrix4x4_ToArray12FloatGL(&posematrix, mtest);
                        AnglesFromVectors(angles, mtest[0], mtest[2], false);
                        if (angles[0] >= 180) angles[0] -= 360;
                        if (angles[1] >= 180) angles[1] -= 360;
@@ -3333,7 +3339,7 @@ static void Mod_Decompile_f(cmd_state_t *cmd)
                return;
        }
 
-       strlcpy(inname, Cmd_Argv(cmd, 1), sizeof(inname));
+       dp_strlcpy(inname, Cmd_Argv(cmd, 1), sizeof(inname));
        FS_StripExtension(inname, basename, sizeof(basename));
 
        mod = Mod_ForName(inname, false, true, inname[0] == '*' ? cl.model_name[1] : NULL);
@@ -3374,7 +3380,7 @@ static void Mod_Decompile_f(cmd_state_t *cmd)
                if (l > 0) dpmtextsize += l;
                for (i = 0;i < mod->numframes;i = j)
                {
-                       strlcpy(animname, mod->animscenes[i].name, sizeof(animname));
+                       dp_strlcpy(animname, mod->animscenes[i].name, sizeof(animname));
                        first = mod->animscenes[i].firstframe;
                        if (mod->animscenes[i].framecount > 1)
                        {
@@ -3395,7 +3401,7 @@ static void Mod_Decompile_f(cmd_state_t *cmd)
                                count = mod->num_poses - first;
                                for (j = i + 1;j < mod->numframes;j++)
                                {
-                                       strlcpy(animname2, mod->animscenes[j].name, sizeof(animname2));
+                                       dp_strlcpy(animname2, mod->animscenes[j].name, sizeof(animname2));
                                        for (l = 0, k = (int)strlen(animname2);animname2[l];l++)
                                                if(animname2[l] < '0' || animname2[l] > '9')
                                                        k = l + 1;
@@ -3410,7 +3416,7 @@ static void Mod_Decompile_f(cmd_state_t *cmd)
                                }
                                // if it's only one frame, use the original frame name
                                if (j == i + 1)
-                                       strlcpy(animname, mod->animscenes[i].name, sizeof(animname));
+                                       dp_strlcpy(animname, mod->animscenes[i].name, sizeof(animname));
                                
                        }
                        dpsnprintf(outname, sizeof(outname), "%s_decompiled/%s.smd", basename, animname);
@@ -4437,7 +4443,7 @@ static void Mod_GenerateLightmaps_f(cmd_state_t *cmd)
 void Mod_Mesh_Create(model_t *mod, const char *name)
 {
        memset(mod, 0, sizeof(*mod));
-       strlcpy(mod->name, name, sizeof(mod->name));
+       dp_strlcpy(mod->name, name, sizeof(mod->name));
        mod->mempool = Mem_AllocPool(name, 0, NULL);
        mod->texturepool = R_AllocTexturePool();
        mod->Draw = R_Mod_Draw;
@@ -4461,7 +4467,8 @@ void Mod_Mesh_Reset(model_t *mod)
        mod->num_surfaces = 0;
        mod->surfmesh.num_vertices = 0;
        mod->surfmesh.num_triangles = 0;
-       memset(mod->surfmesh.data_vertexhash, -1, mod->surfmesh.num_vertexhashsize * sizeof(*mod->surfmesh.data_vertexhash));
+       if (mod->surfmesh.data_vertexhash) // UBSan: memset arg 1 isn't allowed to be null, but sometimes this is NULL.
+               memset(mod->surfmesh.data_vertexhash, -1, mod->surfmesh.num_vertexhashsize * sizeof(*mod->surfmesh.data_vertexhash));
        mod->DrawSky = NULL; // will be set if a texture needs it
        mod->DrawAddWaterPlanes = NULL; // will be set if a texture needs it
 }
index cf5e02dc1ec4450210fceb52f9767a01ca738caf..39572b5408315dc02d7dd7a8bba25a8483849620 100644 (file)
@@ -442,7 +442,7 @@ typedef struct model_s
        // all models use textures...
        rtexturepool_t  *texturepool;
        // EF_* flags (translated from the model file's different flags layout)
-       int                             effects;
+       unsigned                                effects;
        // number of QC accessible frame(group)s in the model
        int                             numframes;
        // number of QC accessible skin(group)s in the model
index 44d8f0aee893c34bfbd4bc07b52ec3d401189a6a..5b2e36d7cc72a1346723fed30e7519222da7086e 100644 (file)
@@ -298,6 +298,7 @@ void Mod_IDSP_Load(model_t *mod, void *buffer, void *bufferend)
                unsigned char palette[256][4];
                const unsigned char *in;
                dspritehl_t *pinhlsprite;
+               unsigned char *aligneddata;
 
                pinhlsprite = (dspritehl_t *)datapointer;
                datapointer += sizeof(dspritehl_t);
@@ -361,7 +362,11 @@ void Mod_IDSP_Load(model_t *mod, void *buffer, void *bufferend)
                        return;
                }
 
-               Mod_Sprite_SharedSetup(datapointer, LittleLong (pinhlsprite->version), (unsigned int *)(&palette[0][0]), rendermode == SPRHL_ADDITIVE);
+               // the above datapointer arithmetic causes misaligned access
+               aligneddata = (unsigned char *)Mem_Alloc(tempmempool, (unsigned char *)bufferend - datapointer);
+               memcpy(aligneddata, datapointer, (unsigned char *)bufferend - datapointer);
+               Mod_Sprite_SharedSetup(aligneddata, LittleLong (pinhlsprite->version), (unsigned int *)(&palette[0][0]), rendermode == SPRHL_ADDITIVE);
+               Mem_Free(aligneddata);
        }
        else
                Host_Error("Mod_IDSP_Load: %s has wrong version number (%i). Only %i (quake), %i (HalfLife), and %i (sprite32) supported",
index 69801a04a7078fa7d4916f0da00277f1fb69983e..41c0d1bb82f8d7e0f6ed04cffc35ff68ec59354e 100644 (file)
@@ -53,10 +53,10 @@ const char *vm_m_extensions[] = {
 NULL
 };
 
-qbool MP_ConsoleCommand(const char *text)
+qbool MP_ConsoleCommand(const char *text, size_t textlen)
 {
        prvm_prog_t *prog = MVM_prog;
-       return PRVM_ConsoleCommand(prog, text, &prog->funcoffsets.GameCommand, false, -1, 0, prog->loaded, "QC function GameCommand is missing");
+       return PRVM_ConsoleCommand(prog, text, textlen, &prog->funcoffsets.GameCommand, false, -1, 0, "QC function GameCommand is missing");
 }
 
 /*
@@ -193,19 +193,10 @@ static void VM_M_getresolution(prvm_prog_t *prog)
        }
        else if(nr == -1)
        {
-               vid_mode_t *m = VID_GetDesktopMode();
-               if (m)
-               {
-                       PRVM_G_VECTOR(OFS_RETURN)[0] = m->width;
-                       PRVM_G_VECTOR(OFS_RETURN)[1] = m->height;
-                       PRVM_G_VECTOR(OFS_RETURN)[2] = m->pixelheight_num / (prvm_vec_t) m->pixelheight_denom;
-               }
-               else
-               {
-                       PRVM_G_VECTOR(OFS_RETURN)[0] = 0;
-                       PRVM_G_VECTOR(OFS_RETURN)[1] = 0;
-                       PRVM_G_VECTOR(OFS_RETURN)[2] = 0;
-               }
+               vid_mode_t m = VID_GetDesktopMode();
+               PRVM_G_VECTOR(OFS_RETURN)[0] = m.width;
+               PRVM_G_VECTOR(OFS_RETURN)[1] = m.height;
+               PRVM_G_VECTOR(OFS_RETURN)[2] = m.pixelheight_num / (prvm_vec_t) m.pixelheight_denom;
        }
        else
        {
@@ -229,9 +220,9 @@ static void VM_M_getgamedirinfo(prvm_prog_t *prog)
        if(nr >= 0 && nr < fs_all_gamedirs_count)
        {
                if(item == 0)
-                       PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString( prog, fs_all_gamedirs[nr].name );
+                       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, fs_all_gamedirs[nr].name, strlen(fs_all_gamedirs[nr].name));
                else if(item == 1)
-                       PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString( prog, fs_all_gamedirs[nr].description );
+                       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, fs_all_gamedirs[nr].description, strlen(fs_all_gamedirs[nr].description));
        }
 }
 
@@ -340,25 +331,25 @@ static void VM_M_setserverlistmaskstring(prvm_prog_t *prog)
 
        switch( field ) {
                case SLIF_CNAME:
-                       strlcpy( mask->info.cname, str, sizeof(mask->info.cname) );
+                       mask->info.cname_len = dp_strlcpy(mask->info.cname, str, sizeof(mask->info.cname));
                        break;
                case SLIF_NAME:
-                       strlcpy( mask->info.name, str, sizeof(mask->info.name)  );
+                       mask->info.name_len = dp_strlcpy(mask->info.name, str, sizeof(mask->info.name));
                        break;
                case SLIF_QCSTATUS:
-                       strlcpy( mask->info.qcstatus, str, sizeof(mask->info.qcstatus)  );
+                       mask->info.qcstatus_len = dp_strlcpy(mask->info.qcstatus, str, sizeof(mask->info.qcstatus));
                        break;
                case SLIF_PLAYERS:
-                       strlcpy( mask->info.players, str, sizeof(mask->info.players)  );
+                       mask->info.players_len = dp_strlcpy(mask->info.players, str, sizeof(mask->info.players));
                        break;
                case SLIF_MAP:
-                       strlcpy( mask->info.map, str, sizeof(mask->info.map)  );
+                       mask->info.map_len = dp_strlcpy(mask->info.map, str, sizeof(mask->info.map));
                        break;
                case SLIF_MOD:
-                       strlcpy( mask->info.mod, str, sizeof(mask->info.mod)  );
+                       mask->info.mod_len = dp_strlcpy(mask->info.mod, str, sizeof(mask->info.mod));
                        break;
                case SLIF_GAME:
-                       strlcpy( mask->info.game, str, sizeof(mask->info.game)  );
+                       mask->info.game_len = dp_strlcpy(mask->info.game, str, sizeof(mask->info.game));
                        break;
                default:
                        VM_Warning(prog, "VM_M_setserverlistmaskstring: Bad field number %i passed!\n", field );
@@ -449,7 +440,7 @@ resortserverlist
 static void VM_M_resortserverlist(prvm_prog_t *prog)
 {
        VM_SAFEPARMCOUNT(0, VM_M_resortserverlist);
-       ServerList_RebuildViewList();
+       ServerList_RebuildViewList(NULL);
 }
 
 /*
@@ -476,7 +467,7 @@ static void VM_M_getserverliststring(prvm_prog_t *prog)
        }
        else
        {
-               if(hostnr < 0 || hostnr >= serverlist_viewcount)
+               if(hostnr < 0 || (unsigned)hostnr >= serverlist_viewcount)
                {
                        Con_Print("VM_M_getserverliststring: bad hostnr passed!\n");
                        return;
@@ -485,32 +476,32 @@ static void VM_M_getserverliststring(prvm_prog_t *prog)
        }
        switch( (int) PRVM_G_FLOAT(OFS_PARM0) ) {
                case SLIF_CNAME:
-                       PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString( prog, cache->info.cname );
+                       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, cache->info.cname, cache->info.cname_len);
                        break;
                case SLIF_NAME:
-                       PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString( prog, cache->info.name );
+                       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, cache->info.name, cache->info.name_len);
                        break;
                case SLIF_QCSTATUS:
-                       PRVM_G_INT (OFS_RETURN ) = PRVM_SetTempString( prog, cache->info.qcstatus );
+                       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, cache->info.qcstatus, cache->info.qcstatus_len);
                        break;
                case SLIF_PLAYERS:
-                       PRVM_G_INT (OFS_RETURN ) = PRVM_SetTempString( prog, cache->info.players );
+                       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, cache->info.players, cache->info.players_len);
                        break;
                case SLIF_GAME:
-                       PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString( prog, cache->info.game );
+                       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, cache->info.game, cache->info.game_len);
                        break;
                case SLIF_MOD:
-                       PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString( prog, cache->info.mod );
+                       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, cache->info.mod, cache->info.mod_len);
                        break;
                case SLIF_MAP:
-                       PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString( prog, cache->info.map );
+                       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, cache->info.map, cache->info.map_len);
                        break;
                // TODO remove this again
                case 1024:
-                       PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString( prog, cache->line1 );
+                       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, cache->line1, cache->line1_len);
                        break;
                case 1025:
-                       PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString( prog, cache->line2 );
+                       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, cache->line2, cache->line2_len);
                        break;
                default:
                        Con_Print("VM_M_getserverliststring: bad field number passed!\n");
@@ -541,7 +532,7 @@ static void VM_M_getserverlistnumber(prvm_prog_t *prog)
        }
        else
        {
-               if(hostnr < 0 || hostnr >= serverlist_viewcount)
+               if(hostnr < 0 || (unsigned)hostnr >= serverlist_viewcount)
                {
                        Con_Print("VM_M_getserverliststring: bad hostnr passed!\n");
                        return;
@@ -565,7 +556,8 @@ static void VM_M_getserverlistnumber(prvm_prog_t *prog)
                        PRVM_G_FLOAT( OFS_RETURN ) = cache->info.freeslots;
                        break;
                case SLIF_PING:
-                       PRVM_G_FLOAT( OFS_RETURN ) = cache->info.ping;
+                       // display inf when a listed server times out and net_slist_pause blocks its removal
+                       PRVM_G_FLOAT( OFS_RETURN ) = cache->info.ping ? cache->info.ping : INFINITY;
                        break;
                case SLIF_PROTOCOL:
                        PRVM_G_FLOAT( OFS_RETURN ) = cache->info.protocol;
@@ -818,7 +810,7 @@ static void VM_M_crypto_getkeyfp(prvm_prog_t *prog)
        VM_CheckEmptyString( prog, s );
 
        if(LHNETADDRESS_FromString(&addr, s, 26000) && Crypto_RetrieveHostKey(&addr, NULL, keyfp, sizeof(keyfp), NULL, 0, NULL, NULL))
-               PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString( prog, keyfp );
+               PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, keyfp, strlen(keyfp));
        else
                PRVM_G_INT( OFS_RETURN ) = OFS_NULL;
 }
@@ -834,7 +826,7 @@ static void VM_M_crypto_getidfp(prvm_prog_t *prog)
        VM_CheckEmptyString( prog, s );
 
        if(LHNETADDRESS_FromString(&addr, s, 26000) && Crypto_RetrieveHostKey(&addr, NULL, NULL, 0, idfp, sizeof(idfp), NULL, NULL))
-               PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString( prog, idfp );
+               PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString( prog, idfp, strlen(idfp));
        else
                PRVM_G_INT( OFS_RETURN ) = OFS_NULL;
 }
@@ -867,7 +859,15 @@ static void VM_M_crypto_getencryptlevel(prvm_prog_t *prog)
        VM_CheckEmptyString( prog, s );
 
        if(LHNETADDRESS_FromString(&addr, s, 26000) && Crypto_RetrieveHostKey(&addr, NULL, NULL, 0, NULL, 0, &aeslevel, NULL))
-               PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString(prog, aeslevel ? va(vabuf, sizeof(vabuf), "%d AES128", aeslevel) : "0");
+       {
+               if (aeslevel)
+               {
+                       size_t vabuf_len = dpsnprintf(vabuf, sizeof(vabuf), "%d AES128", aeslevel);
+                       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, vabuf, vabuf_len);
+               }
+               else
+                       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, "0", 1);
+       }
        else
                PRVM_G_INT( OFS_RETURN ) = OFS_NULL;
 }
@@ -882,14 +882,14 @@ static void VM_M_crypto_getmykeyfp(prvm_prog_t *prog)
        switch(Crypto_RetrieveLocalKey(i, keyfp, sizeof(keyfp), NULL, 0, NULL))
        {
                case -1:
-                       PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString(prog, "");
+                       PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString(prog, "", 0);
                        break;
                case 0:
                        PRVM_G_INT( OFS_RETURN ) = OFS_NULL;
                        break;
                default:
                case 1:
-                       PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString(prog, keyfp);
+                       PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString(prog, keyfp, strlen(keyfp));
                        break;
        }
 }
@@ -904,14 +904,14 @@ static void VM_M_crypto_getmyidfp(prvm_prog_t *prog)
        switch(Crypto_RetrieveLocalKey(i, NULL, 0, idfp, sizeof(idfp), NULL))
        {
                case -1:
-                       PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString(prog, "");
+                       PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString(prog, "", 0);
                        break;
                case 0:
                        PRVM_G_INT( OFS_RETURN ) = OFS_NULL;
                        break;
                default:
                case 1:
-                       PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString(prog, idfp);
+                       PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString(prog, idfp, strlen(idfp));
                        break;
        }
 }
@@ -1058,8 +1058,7 @@ void VM_cin_restart(prvm_prog_t *prog)
 static void VM_M_registercommand(prvm_prog_t *prog)
 {
        VM_SAFEPARMCOUNT(1, VM_M_registercommand);
-       if(!Cmd_Exists(cmd_local, PRVM_G_STRING(OFS_PARM0)))
-               Cmd_AddCommand(CF_CLIENT, PRVM_G_STRING(OFS_PARM0), NULL, "console command created by QuakeC");
+       Cmd_AddCommand(CF_CLIENT, PRVM_G_STRING(OFS_PARM0), NULL, "console command created by QuakeC");
 }
 
 prvm_builtin_t vm_m_builtins[] = {
@@ -1076,7 +1075,7 @@ VM_vlen,                                                          //   #9
 VM_vectoyaw,                                           //  #10
 VM_vectoangles,                                        //  #11
 VM_random,                                                     //  #12
-VM_localcmd_local,                                             //  #13
+VM_localcmd,                                           //  #13
 VM_cvar,                                                               //  #14
 VM_cvar_set,                                           //  #15
 VM_dprint,                                                     //  #16
old mode 100755 (executable)
new mode 100644 (file)
index 98d2601..4ff6a57
--- a/netconn.c
+++ b/netconn.c
@@ -44,23 +44,22 @@ static cvar_t sv_masters [] =
        {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sv_master2", "", "user-chosen master server 2"},
        {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sv_master3", "", "user-chosen master server 3"},
        {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sv_master4", "", "user-chosen master server 4"},
-       {CF_CLIENT | CF_SERVER, "sv_masterextra1", "dpmaster.deathmask.net", "dpmaster.deathmask.net - default master server 1 (admin: Willis)"}, // admin: Willis
-       {CF_CLIENT | CF_SERVER, "sv_masterextra2", "dpmaster.tchr.no", "dpmaster.tchr.no - default master server 2 (admin: tChr)"}, // admin: tChr
-       {0, NULL, NULL, NULL}
+       {CF_CLIENT | CF_SERVER, "sv_masterextra1", "dpmaster.deathmask.net", "dpmaster.deathmask.net - default master server 1 (admin: Willis)"},
+       {CF_CLIENT | CF_SERVER, "sv_masterextra2", "dpmaster.tchr.no", "dpmaster.tchr.no - default master server 2 (admin: tChr)"},
+       {CF_CLIENT | CF_SERVER, "sv_masterextra3", "dpm.dpmaster.org:27777", "dpm.dpmaster.org - default master server 3 (admin: gazby/soylent_cow)"},
 };
 
 #ifdef CONFIG_MENU
 static cvar_t sv_qwmasters [] =
 {
-       {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sv_qwmaster1", "", "user-chosen qwmaster server 1"},
-       {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sv_qwmaster2", "", "user-chosen qwmaster server 2"},
-       {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sv_qwmaster3", "", "user-chosen qwmaster server 3"},
-       {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sv_qwmaster4", "", "user-chosen qwmaster server 4"},
-       {CF_CLIENT | CF_SERVER, "sv_qwmasterextra1", "master.quakeservers.net:27000", "Global master server. (admin: unknown)"},
-       {CF_CLIENT | CF_SERVER, "sv_qwmasterextra2", "asgaard.morphos-team.net:27000", "Global master server. (admin: unknown)"},
-       {CF_CLIENT | CF_SERVER, "sv_qwmasterextra3", "qwmaster.ocrana.de:27000", "German master server. (admin: unknown)"},
-       {CF_CLIENT | CF_SERVER, "sv_qwmasterextra4", "qwmaster.fodquake.net:27000", "Global master server. (admin: unknown)"},
-       {0, NULL, NULL, NULL}
+       {CF_CLIENT | CF_ARCHIVE, "sv_qwmaster1", "", "user-chosen qwmaster server 1"},
+       {CF_CLIENT | CF_ARCHIVE, "sv_qwmaster2", "", "user-chosen qwmaster server 2"},
+       {CF_CLIENT | CF_ARCHIVE, "sv_qwmaster3", "", "user-chosen qwmaster server 3"},
+       {CF_CLIENT | CF_ARCHIVE, "sv_qwmaster4", "", "user-chosen qwmaster server 4"},
+       {CF_CLIENT, "sv_qwmasterextra1", "master.quakeservers.net:27000", "Global master server. (admin: unknown)"},
+       {CF_CLIENT, "sv_qwmasterextra2", "asgaard.morphos-team.net:27000", "Global master server. (admin: unknown)"},
+       {CF_CLIENT, "sv_qwmasterextra3", "qwmaster.ocrana.de:27000", "German master server. (admin: unknown)"},
+       {CF_CLIENT, "sv_qwmasterextra4", "qwmaster.fodquake.net:27000", "Global master server. (admin: unknown)"},
 };
 #endif
 
@@ -80,7 +79,7 @@ cvar_t net_messagetimeout = {CF_CLIENT | CF_SERVER, "net_messagetimeout","300",
 cvar_t net_connecttimeout = {CF_CLIENT | CF_SERVER, "net_connecttimeout","15", "after requesting a connection, the client must reply within this many seconds or be dropped (cuts down on connect floods). Must be above 10 seconds."};
 cvar_t net_connectfloodblockingtimeout = {CF_SERVER, "net_connectfloodblockingtimeout", "5", "when a connection packet is received, it will block all future connect packets from that IP address for this many seconds (cuts down on connect floods). Note that this does not include retries from the same IP; these are handled earlier and let in."};
 cvar_t net_challengefloodblockingtimeout = {CF_SERVER, "net_challengefloodblockingtimeout", "0.5", "when a challenge packet is received, it will block all future challenge packets from that IP address for this many seconds (cuts down on challenge floods). DarkPlaces clients retry once per second, so this should be <= 1. Failure here may lead to connect attempts failing."};
-cvar_t net_getstatusfloodblockingtimeout = {CF_SERVER, "net_getstatusfloodblockingtimeout", "1", "when a getstatus packet is received, it will block all future getstatus packets from that IP address for this many seconds (cuts down on getstatus floods). DarkPlaces retries every 4 seconds, and qstat retries once per second, so this should be <= 1. Failure here may lead to server not showing up in the server list."};
+cvar_t net_getstatusfloodblockingtimeout = {CF_SERVER, "net_getstatusfloodblockingtimeout", "1", "when a getstatus packet is received, it will block all future getstatus packets from that IP address for this many seconds (cuts down on getstatus floods). DarkPlaces retries every net_slist_timeout seconds, and qstat retries once per second, so this should be <= 1. Failure here may lead to server not showing up in the server list."};
 cvar_t net_sourceaddresscheck = {CF_CLIENT, "net_sourceaddresscheck", "1", "compare the source IP address for replies (more secure, may break some bad multihoming setups"};
 cvar_t hostname = {CF_SERVER | CF_ARCHIVE, "hostname", "UNNAMED", "server message to show in server browser"};
 cvar_t developer_networking = {CF_CLIENT | CF_SERVER, "developer_networking", "0", "prints all received and sent packets (recommended only for debugging)"};
@@ -88,12 +87,19 @@ cvar_t developer_networking = {CF_CLIENT | CF_SERVER, "developer_networking", "0
 cvar_t net_fakelag = {CF_CLIENT, "net_fakelag","0", "lags local loopback connection by this much ping time (useful to play more fairly on your own server with people with higher pings)"};
 static cvar_t net_fakeloss_send = {CF_CLIENT, "net_fakeloss_send","0", "drops this percentage of outgoing packets, useful for testing network protocol robustness (jerky movement, prediction errors, etc)"};
 static cvar_t net_fakeloss_receive = {CF_CLIENT, "net_fakeloss_receive","0", "drops this percentage of incoming packets, useful for testing network protocol robustness (jerky movement, effects failing to start, sounds failing to play, etc)"};
-static cvar_t net_slist_queriespersecond = {CF_CLIENT, "net_slist_queriespersecond", "20", "how many server information requests to send per second"};
-static cvar_t net_slist_queriesperframe = {CF_CLIENT, "net_slist_queriesperframe", "4", "maximum number of server information requests to send each rendered frame (guards against low framerates causing problems)"};
-static cvar_t net_slist_timeout = {CF_CLIENT, "net_slist_timeout", "4", "how long to listen for a server information response before giving up"};
-static cvar_t net_slist_pause = {CF_CLIENT, "net_slist_pause", "0", "when set to 1, the server list won't update until it is set back to 0"};
-static cvar_t net_slist_maxtries = {CF_CLIENT, "net_slist_maxtries", "3", "how many times to ask the same server for information (more times gives better ping reports but takes longer)"};
+
+#ifdef CONFIG_MENU
+static cvar_t net_slist_debug = {CF_CLIENT, "net_slist_debug", "0", "enables verbose messages for master server queries"};
 static cvar_t net_slist_favorites = {CF_CLIENT | CF_ARCHIVE, "net_slist_favorites", "", "contains a list of IP addresses and ports to always query explicitly"};
+static cvar_t net_slist_interval = {CF_CLIENT, "net_slist_interval", "1", "minimum number of seconds to wait between getstatus queries to the same DP server, must be >= server's net_getstatusfloodblockingtimeout"};
+static cvar_t net_slist_maxping = {CF_CLIENT | CF_ARCHIVE, "net_slist_maxping", "420", "server query responses are ignored if their ping in milliseconds is higher than this"};
+static cvar_t net_slist_maxtries = {CF_CLIENT, "net_slist_maxtries", "3", "how many times to ask the same server for information (more times gives better ping reports but takes longer)"};
+static cvar_t net_slist_pause = {CF_CLIENT, "net_slist_pause", "0", "when set to 1, the server list sorting in the menu won't update until it is set back to 0"};
+static cvar_t net_slist_queriespersecond = {CF_CLIENT, "net_slist_queriespersecond", "128", "how many server information requests to send per second"};
+static cvar_t net_slist_queriesperframe = {CF_CLIENT, "net_slist_queriesperframe", "2", "maximum number of server information requests to send each rendered frame (guards against low framerates causing problems)"};
+static cvar_t net_slist_timeout = {CF_CLIENT, "net_slist_timeout", "4", "minimum number of seconds to wait between status queries to the same QW server, determines which response belongs to which query so low values will cause impossible pings; also a warning is printed if a dpmaster query fails to complete within this time"};
+#endif
+
 static cvar_t net_tos_dscp = {CF_CLIENT | CF_ARCHIVE, "net_tos_dscp", "32", "DiffServ Codepoint for network sockets (may need game restart to apply)"};
 static cvar_t gameversion = {CF_SERVER, "gameversion", "0", "version of game data (mod-specific) to be sent to querying clients"};
 static cvar_t gameversion_min = {CF_CLIENT | CF_SERVER, "gameversion_min", "-1", "minimum version of game data (mod-specific), when client and server gameversion mismatch in the server browser the server is shown as incompatible; if -1, gameversion is used alone"};
@@ -105,26 +111,37 @@ extern cvar_t rcon_secure;
 extern cvar_t rcon_secure_challengetimeout;
 
 double masterquerytime = -1000;
-int masterquerycount = 0;
-int masterreplycount = 0;
-int serverquerycount = 0;
-int serverreplycount = 0;
+unsigned masterquerycount = 0;
+unsigned masterreplycount = 0;
+unsigned serverquerycount = 0;
+unsigned serverreplycount = 0;
 
 challenge_t challenges[MAX_CHALLENGES];
 
+#define DPMASTER_COUNT sizeof(sv_masters) / sizeof(cvar_t)
+#define QWMASTER_COUNT sizeof(sv_qwmasters) / sizeof(cvar_t)
+
+/// bitfield because in theory we could be doing QW & DP simultaneously
+uint8_t serverlist_querystage = 0;
+
 #ifdef CONFIG_MENU
-/// this is only false if there are still servers left to query
-static qbool serverlist_querysleep = true;
-static qbool serverlist_paused = false;
-/// this is pushed a second or two ahead of realtime whenever a master server
-/// reply is received, to avoid issuing queries while master replies are still
-/// flooding in (which would make a mess of the ping times)
-static double serverlist_querywaittime = 0;
+#define SLIST_QUERYSTAGE_DPMASTERS 1
+#define SLIST_QUERYSTAGE_QWMASTERS 2
+#define SLIST_QUERYSTAGE_SERVERS 4
+
+static uint8_t dpmasterstatus[DPMASTER_COUNT] = {0};
+static uint8_t qwmasterstatus[QWMASTER_COUNT] = {0};
+#define MASTER_TX_QUERY 1    // we sent the query
+#define MASTER_RX_RESPONSE 2 // we got at least 1 packet of the response
+#define MASTER_RX_COMPLETE 3 // we saw the EOT marker (assumes dpmaster >= 2.0, see dpmaster/doc/techinfo.txt)
+
+/// the hash password for timestamp verification
+char serverlist_dpserverquerykey[12]; // challenge_t uses [12]
 #endif
 
-static int cl_numsockets;
+static unsigned cl_numsockets;
 static lhnetsocket_t *cl_sockets[16];
-static int sv_numsockets;
+static unsigned sv_numsockets;
 static lhnetsocket_t *sv_sockets[16];
 
 netconn_t *netconn_list = NULL;
@@ -137,12 +154,12 @@ cvar_t net_address = {CF_CLIENT | CF_SERVER, "net_address", "", "network address
 cvar_t net_address_ipv6 = {CF_CLIENT | CF_SERVER, "net_address_ipv6", "", "network address to open ipv6 ports on (if empty, use default interfaces)"};
 
 char cl_net_extresponse[NET_EXTRESPONSE_MAX][1400];
-int cl_net_extresponse_count = 0;
-int cl_net_extresponse_last = 0;
+unsigned cl_net_extresponse_count = 0;
+unsigned cl_net_extresponse_last = 0;
 
 char sv_net_extresponse[NET_EXTRESPONSE_MAX][1400];
-int sv_net_extresponse_count = 0;
-int sv_net_extresponse_last = 0;
+unsigned sv_net_extresponse_count = 0;
+unsigned sv_net_extresponse_last = 0;
 
 #ifdef CONFIG_MENU
 // ServerList interface
@@ -150,20 +167,20 @@ serverlist_mask_t serverlist_andmasks[SERVERLIST_ANDMASKCOUNT];
 serverlist_mask_t serverlist_ormasks[SERVERLIST_ORMASKCOUNT];
 
 serverlist_infofield_t serverlist_sortbyfield;
-int serverlist_sortflags;
+unsigned serverlist_sortflags;
 
-int serverlist_viewcount = 0;
-unsigned short serverlist_viewlist[SERVERLIST_VIEWLISTSIZE];
+unsigned serverlist_viewcount = 0;
+uint16_t serverlist_viewlist[SERVERLIST_VIEWLISTSIZE];
 
-int serverlist_maxcachecount = 0;
-int serverlist_cachecount = 0;
+unsigned serverlist_maxcachecount = 0;
+unsigned serverlist_cachecount = 0;
 serverlist_entry_t *serverlist_cache = NULL;
 
-qbool serverlist_consoleoutput;
+static qbool serverlist_consoleoutput;
 
-static int nFavorites = 0;
+static unsigned nFavorites = 0;
 static lhnetaddress_t favorites[MAX_FAVORITESERVERS];
-static int nFavorites_idfp = 0;
+static unsigned nFavorites_idfp = 0;
 static char favorites_idfp[MAX_FAVORITESERVERS][FP64_SIZE+1];
 
 void NetConn_UpdateFavorites_c(cvar_t *var)
@@ -178,7 +195,7 @@ void NetConn_UpdateFavorites_c(cvar_t *var)
                // currently 44 bytes, longest possible IPv6 address: 39 bytes, so this works
                // (if v6 address contains port, it must start with '[')
                {
-                       strlcpy(favorites_idfp[nFavorites_idfp], com_token, sizeof(favorites_idfp[nFavorites_idfp]));
+                       dp_strlcpy(favorites_idfp[nFavorites_idfp], com_token, sizeof(favorites_idfp[nFavorites_idfp]));
                        ++nFavorites_idfp;
                }
                else 
@@ -191,9 +208,10 @@ void NetConn_UpdateFavorites_c(cvar_t *var)
 
 /// helper function to insert a value into the viewset
 /// spare entries will be removed
-static void _ServerList_ViewList_Helper_InsertBefore( int index, serverlist_entry_t *entry )
+static void _ServerList_ViewList_Helper_InsertBefore(unsigned index, serverlist_entry_t *entry)
 {
-    int i;
+       unsigned i;
+
        if( serverlist_viewcount < SERVERLIST_VIEWLISTSIZE ) {
                i = serverlist_viewcount++;
        } else {
@@ -207,7 +225,7 @@ static void _ServerList_ViewList_Helper_InsertBefore( int index, serverlist_entr
 }
 
 /// we suppose serverlist_viewcount to be valid, ie > 0
-static void _ServerList_ViewList_Helper_Remove( int index )
+static inline void _ServerList_ViewList_Helper_Remove(unsigned index)
 {
        serverlist_viewcount--;
        for( ; index < serverlist_viewcount ; index++ )
@@ -411,7 +429,7 @@ static qbool _ServerList_Entry_Mask( serverlist_mask_t *mask, serverlist_info_t
 
 static void ServerList_ViewList_Insert( serverlist_entry_t *entry )
 {
-       int start, end, mid, i;
+       unsigned start, end, mid, i;
        lhnetaddress_t addr;
 
        // reject incompatible servers
@@ -427,6 +445,11 @@ static void ServerList_ViewList_Insert( serverlist_entry_t *entry )
        )
                return;
 
+       // also display entries that are currently being refreshed [11/8/2007 Black]
+       // bones_was_here: if their previous ping was acceptable (unset if timeout occurs)
+       if (!entry->info.ping)
+               return;
+
        // refresh the "favorite" status
        entry->info.isfavorite = false;
        if(LHNETADDRESS_FromString(&addr, entry->info.cname, 26000))
@@ -502,7 +525,8 @@ static void ServerList_ViewList_Insert( serverlist_entry_t *entry )
 
 static void ServerList_ViewList_Remove( serverlist_entry_t *entry )
 {
-       int i;
+       unsigned i;
+
        for( i = 0; i < serverlist_viewcount; i++ )
        {
                if (ServerList_GetViewEntry(i) == entry)
@@ -513,17 +537,16 @@ static void ServerList_ViewList_Remove( serverlist_entry_t *entry )
        }
 }
 
-void ServerList_RebuildViewList(void)
+void ServerList_RebuildViewList(cvar_t *var)
 {
-       int i;
+       unsigned i;
+
+       if (net_slist_pause.integer)
+               return;
 
        serverlist_viewcount = 0;
-       for( i = 0 ; i < serverlist_cachecount ; i++ ) {
-               serverlist_entry_t *entry = &serverlist_cache[i];
-               // also display entries that are currently being refreshed [11/8/2007 Black]
-               if( entry->query == SQS_QUERIED || entry->query == SQS_REFRESHING )
-                       ServerList_ViewList_Insert( entry );
-       }
+       for (i = 0; i < serverlist_cachecount; ++i)
+               ServerList_ViewList_Insert(&serverlist_cache[i]);
 }
 
 void ServerList_ResetMasks(void)
@@ -539,13 +562,14 @@ void ServerList_ResetMasks(void)
                serverlist_ormasks[i].info.numbots = -1;
 }
 
-void ServerList_GetPlayerStatistics(int *numplayerspointer, int *maxplayerspointer)
+void ServerList_GetPlayerStatistics(unsigned *numplayerspointer, unsigned *maxplayerspointer)
 {
-       int i;
-       int numplayers = 0, maxplayers = 0;
+       unsigned i;
+       unsigned numplayers = 0, maxplayers = 0;
+
        for (i = 0;i < serverlist_cachecount;i++)
        {
-               if (serverlist_cache[i].query == SQS_QUERIED)
+               if (serverlist_cache[i].info.ping)
                {
                        numplayers += serverlist_cache[i].info.numhumans;
                        maxplayers += serverlist_cache[i].info.maxplayers;
@@ -576,25 +600,62 @@ static void _ServerList_Test(void)
 }
 #endif
 
+/*
+====================
+ServerList_BuildDPServerQuery
+
+Generates the string for pinging DP servers with a hash-verified timestamp
+to provide reliable pings while preventing ping cheating,
+and discard spurious getstatus/getinfo packets.
+
+14 bytes of header including the mandatory space,
+22 bytes of base64-encoded hash,
+up to 16 bytes of unsigned hexadecimal milliseconds (typically 4-5 bytes sent),
+null terminator.
+
+The maximum challenge length (after the space) for existing DP7 servers is 49.
+====================
+*/
+static inline void ServerList_BuildDPServerQuery(char *buffer, size_t buffersize, double querytime)
+{
+       unsigned char hash[24]; // 4*(16/3) rounded up to 4 byte multiple
+       uint64_t timestamp = querytime * 1000.0; // no rounding up as that could make small pings go <= 0
+
+       HMAC_MDFOUR_16BYTES(hash,
+               (unsigned char *)&timestamp, sizeof(timestamp),
+               (unsigned char *)serverlist_dpserverquerykey, sizeof(serverlist_dpserverquerykey));
+       base64_encode(hash, 16, sizeof(hash));
+       dpsnprintf(buffer, buffersize, "\377\377\377\377getstatus %.22s%" PRIx64, hash, timestamp);
+}
+
+static void NetConn_BuildChallengeString(char *buffer, int bufferlength);
 void ServerList_QueryList(qbool resetcache, qbool querydp, qbool queryqw, qbool consoleoutput)
 {
-       masterquerytime = host.realtime;
+       unsigned i;
+       lhnetaddress_t broadcastaddress;
+       char dpquery[53]; // theoretical max: 14+22+16+1
+
+       if (net_slist_debug.integer)
+               Con_Printf("^2Querying master, favourite and LAN servers, reset=%u\n", resetcache);
+       serverlist_querystage = (querydp ? SLIST_QUERYSTAGE_DPMASTERS : 0) | (queryqw ? SLIST_QUERYSTAGE_QWMASTERS : 0);
        masterquerycount = 0;
        masterreplycount = 0;
-       if( resetcache ) {
+       if (resetcache)
+       {
                serverquerycount = 0;
                serverreplycount = 0;
                serverlist_cachecount = 0;
                serverlist_viewcount = 0;
                serverlist_maxcachecount = 0;
                serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
-       } else {
+       }
+       else
+       {
                // refresh all entries
-               int n;
-               for( n = 0 ; n < serverlist_cachecount ; n++ ) {
-                       serverlist_entry_t *entry = &serverlist_cache[ n ];
-                       entry->query = SQS_REFRESHING;
-                       entry->querycounter = 0;
+               for (i = 0; i < serverlist_cachecount; ++i)
+               {
+                       serverlist_entry_t *entry = &serverlist_cache[i];
+                       entry->responded = false;
                }
        }
        serverlist_consoleoutput = consoleoutput;
@@ -602,6 +663,51 @@ void ServerList_QueryList(qbool resetcache, qbool querydp, qbool queryqw, qbool
        //_ServerList_Test();
 
        NetConn_QueryMasters(querydp, queryqw);
+
+       // Generate new DP server query key string
+       // Used to prevent ping cheating and discard spurious getstatus/getinfo packets
+       if (!serverlist_querystage) // don't change key while updating
+               NetConn_BuildChallengeString(serverlist_dpserverquerykey, sizeof(serverlist_dpserverquerykey));
+
+       // LAN search
+
+       // Master and and/or favourite queries were likely delayed by DNS lag,
+       // for correct pings we need to know what host.realtime would be if it were updated now.
+       masterquerytime = host.realtime + (Sys_DirtyTime() - host.dirtytime);
+       ServerList_BuildDPServerQuery(dpquery, sizeof(dpquery), masterquerytime);
+
+       // 26000 is the default quake server port, servers on other ports will not be found
+       // note this is IPv4-only, I doubt there are IPv6-only LANs out there
+       LHNETADDRESS_FromString(&broadcastaddress, "255.255.255.255", 26000);
+
+       for (i = 0; i < cl_numsockets; ++i)
+       {
+               if (!cl_sockets[i])
+                       continue;
+               if (LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) != LHNETADDRESS_GetAddressType(&broadcastaddress))
+                       continue;
+
+               if (querydp)
+               {
+                       // search LAN for Quake servers
+                       SZ_Clear(&cl_message);
+                       // save space for the header, filled in later
+                       MSG_WriteLong(&cl_message, 0);
+                       MSG_WriteByte(&cl_message, CCREQ_SERVER_INFO);
+                       MSG_WriteString(&cl_message, "QUAKE");
+                       MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
+                       StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
+                       NetConn_Write(cl_sockets[i], cl_message.data, cl_message.cursize, &broadcastaddress);
+                       SZ_Clear(&cl_message);
+
+                       // search LAN for DarkPlaces servers
+                       NetConn_WriteString(cl_sockets[i], dpquery, &broadcastaddress);
+               }
+
+               if (queryqw)
+                       // search LAN for QuakeWorld servers
+                       NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &broadcastaddress);
+       }
 }
 #endif
 
@@ -610,7 +716,8 @@ void ServerList_QueryList(qbool resetcache, qbool querydp, qbool queryqw, qbool
 int NetConn_Read(lhnetsocket_t *mysocket, void *data, int maxlength, lhnetaddress_t *peeraddress)
 {
        int length;
-       int i;
+       unsigned i;
+
        if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
                Thread_LockMutex(netconn_mutex);
        length = LHNET_Read(mysocket, data, maxlength, peeraddress);
@@ -641,7 +748,8 @@ int NetConn_Read(lhnetsocket_t *mysocket, void *data, int maxlength, lhnetaddres
 int NetConn_Write(lhnetsocket_t *mysocket, const void *data, int length, const lhnetaddress_t *peeraddress)
 {
        int ret;
-       int i;
+       unsigned i;
+
        if (net_fakeloss_send.integer)
                for (i = 0;i < cl_numsockets;i++)
                        if (cl_sockets[i] == mysocket && (rand() % 100) < net_fakeloss_send.integer)
@@ -1079,7 +1187,9 @@ void NetConn_OpenServerPorts(int opennetports)
 
 lhnetsocket_t *NetConn_ChooseClientSocketForAddress(lhnetaddress_t *address)
 {
-       int i, a = LHNETADDRESS_GetAddressType(address);
+       unsigned i;
+       lhnetaddresstype_t a = LHNETADDRESS_GetAddressType(address);
+
        for (i = 0;i < cl_numsockets;i++)
                if (cl_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == a)
                        return cl_sockets[i];
@@ -1088,7 +1198,9 @@ lhnetsocket_t *NetConn_ChooseClientSocketForAddress(lhnetaddress_t *address)
 
 lhnetsocket_t *NetConn_ChooseServerSocketForAddress(lhnetaddress_t *address)
 {
-       int i, a = LHNETADDRESS_GetAddressType(address);
+       unsigned i;
+       lhnetaddresstype_t a = LHNETADDRESS_GetAddressType(address);
+
        for (i = 0;i < sv_numsockets;i++)
                if (sv_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(sv_sockets[i])) == a)
                        return sv_sockets[i];
@@ -1145,22 +1257,9 @@ void NetConn_Close(netconn_t *conn)
 
 static int clientport = -1;
 static int clientport2 = -1;
-static int hostport = -1;
-
-// Call on disconnect, during startup, or if cl_netport is changed
-void NetConn_UpdateSockets_Client(void)
-{
-       if (cls.state == ca_disconnected && clientport != clientport2)
-       {
-               clientport = clientport2;
-               NetConn_CloseClientPorts();
-       }
-       if (cl_numsockets == 0)
-               NetConn_OpenClientPorts();
-}
 
-// Call when cl_port is changed
-static void NetConn_cl_netport_Callback(cvar_t *var)
+// Call on disconnect, during startup, or if cl_port/cl_netport is changed
+static void NetConn_CL_UpdateSockets_Callback(cvar_t *var)
 {
        if(cls.state != ca_dedicated)
        {
@@ -1170,11 +1269,20 @@ static void NetConn_cl_netport_Callback(cvar_t *var)
                        if (cls.state == ca_connected)
                                Con_Print("Changing \"cl_port\" will not take effect until you reconnect.\n");
                }
-               NetConn_UpdateSockets_Client();
+
+               if (cls.state == ca_disconnected && clientport != clientport2)
+               {
+                       clientport = clientport2;
+                       NetConn_CloseClientPorts();
+               }
+               if (cl_numsockets == 0)
+                       NetConn_OpenClientPorts();
        }
 }
 
-// Call when port is changed
+static int hostport = -1;
+
+// Call when port/sv_netport is changed
 static void NetConn_sv_netport_Callback(cvar_t *var)
 {
        if (hostport != var->integer)
@@ -1245,8 +1353,8 @@ static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, s
                conn->packetsReceived++;
                reliable_message = (sequence >> 31) != 0;
                reliable_ack = (sequence_ack >> 31) != 0;
-               sequence &= ~(1<<31);
-               sequence_ack &= ~(1<<31);
+               sequence &= ~(1u<<31);
+               sequence_ack &= ~(1u<<31);
                if (sequence <= conn->qw.incoming_sequence)
                {
                        //Con_DPrint("Got a stale datagram\n");
@@ -1523,15 +1631,14 @@ static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, s
 static void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, protocolversion_t initialprotocol)
 {
        crypto_t *crypto;
+
        cls.connect_trying = false;
-#ifdef CONFIG_MENU
-       M_Update_Return_Reason("");
-#endif
        // Disconnect from the current server or stop demo playback
        if(cls.state == ca_connected || cls.demoplayback)
                CL_Disconnect();
        // allocate a net connection to keep track of things
        cls.netcon = NetConn_Open(mysocket, peeraddress);
+       dp_strlcpy(cl_connect_status, "Connection established", sizeof(cl_connect_status));
        crypto = &cls.netcon->crypto;
        if(cls.crypto.authenticated)
        {
@@ -1547,7 +1654,9 @@ static void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_
                                crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-"
                                );
        }
-       Con_Printf("Connection accepted to %s\n", cls.netcon->address);
+       else
+               Con_Printf("%s to %s\n", cl_connect_status, cls.netcon->address);
+
        key_dest = key_game;
 #ifdef CONFIG_MENU
        m_state = m_none;
@@ -1582,23 +1691,26 @@ int NetConn_IsLocalGame(void)
 }
 
 #ifdef CONFIG_MENU
-static int NetConn_ClientParsePacket_ServerList_ProcessReply(const char *addressstring)
+static qbool hmac_mdfour_time_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen);
+static int NetConn_ClientParsePacket_ServerList_ProcessReply(const char *addressstring, const char *challenge)
 {
-       int n;
-       int pingtime;
-       serverlist_entry_t *entry = NULL;
+       unsigned n;
+       float ping;
+       double currentrealtime;
+       serverlist_entry_t *entry;
 
        // search the cache for this server and update it
-       for (n = 0;n < serverlist_cachecount;n++) {
-               entry = &serverlist_cache[ n ];
+       for (n = 0; n < serverlist_cachecount; ++n)
+       {
+               entry = &serverlist_cache[n];
                if (!strcmp(addressstring, entry->info.cname))
                        break;
        }
 
        if (n == serverlist_cachecount)
        {
-               // LAN search doesnt require an answer from the master server so we wont
-               // know the ping nor will it be initialized already...
+               if (net_slist_debug.integer)
+                       Con_Printf("^6Received LAN broadcast response from %s\n", addressstring);
 
                // find a slot
                if (serverlist_cachecount == SERVERLIST_TOTALSIZE)
@@ -1609,31 +1721,60 @@ static int NetConn_ClientParsePacket_ServerList_ProcessReply(const char *address
                        serverlist_maxcachecount += 64;
                        serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
                }
+               ++serverlist_cachecount;
                entry = &serverlist_cache[n];
 
                memset(entry, 0, sizeof(*entry));
-               // store the data the engine cares about (address and ping)
-               strlcpy(entry->info.cname, addressstring, sizeof(entry->info.cname));
-               entry->info.ping = 100000;
-               entry->querytime = host.realtime;
-               // if not in the slist menu we should print the server to console
+               entry->info.cname_len = dp_strlcpy(entry->info.cname, addressstring, sizeof(entry->info.cname));
+
+               // use the broadcast as the first query, NetConn_QueryQueueFrame() will send more
+               entry->querytime = masterquerytime;
+               // protocol is one of these at all
+               // NetConn_ClientParsePacket_ServerList_PrepareQuery() callsites
+               entry->protocol = challenge ? PROTOCOL_DARKPLACES7 : PROTOCOL_QUAKEWORLD;
+
                if (serverlist_consoleoutput)
                        Con_Printf("querying %s\n", addressstring);
-               ++serverlist_cachecount;
        }
-       // if this is the first reply from this server, count it as having replied
-       pingtime = (int)((host.realtime - entry->querytime) * 1000.0 + 0.5);
-       pingtime = bound(0, pingtime, 9999);
-       if (entry->query == SQS_REFRESHING) {
-               entry->info.ping = pingtime;
-               entry->query = SQS_QUERIED;
-       } else {
-               // convert to unsigned to catch the -1
-               // I still dont like this but its better than the old 10000 magic ping number - as in easier to type and read :( [11/8/2007 Black]
-               entry->info.ping = min((unsigned) entry->info.ping, (unsigned) pingtime);
+
+       // If the client stalls partway through a frame (test command: `alias a a;a`)
+       // for correct pings we need to know what host.realtime would be if it were updated now.
+       currentrealtime = host.realtime + (Sys_DirtyTime() - host.dirtytime);
+
+       if (challenge)
+       {
+               unsigned char hash[24]; // 4*(16/3) rounded up to 4 byte multiple
+               uint64_t timestamp = strtoull(&challenge[22], NULL, 16);
+
+               HMAC_MDFOUR_16BYTES(hash,
+                       (unsigned char *)&timestamp, sizeof(timestamp),
+                       (unsigned char *)serverlist_dpserverquerykey, sizeof(serverlist_dpserverquerykey));
+               base64_encode(hash, 16, sizeof(hash));
+               if (memcmp(hash, challenge, 22) != 0)
+                       return -1;
+
+               ping = currentrealtime * 1000.0 - timestamp;
+       }
+       else
+               ping = 1000 * (currentrealtime - entry->querytime);
+
+       if (ping <= 0 || ping > net_slist_maxping.value
+       || (entry->info.ping && ping > entry->info.ping + 100)) // server loading map, client stall, etc
+               return -1;
+
+       // never round down to 0, 0 latency is impossible, 0 means no data available
+       if (ping < 1)
+               ping = 1;
+
+       if (entry->info.ping)
+               entry->info.ping = (entry->info.ping + ping) * 0.5 + 0.5; // "average" biased toward most recent results
+       else
+       {
+               entry->info.ping = ping + 0.5;
                serverreplycount++;
        }
-       
+       entry->responded = true;
+
        // other server info is updated by the caller
        return n;
 }
@@ -1642,9 +1783,16 @@ static void NetConn_ClientParsePacket_ServerList_UpdateCache(int n)
 {
        serverlist_entry_t *entry = &serverlist_cache[n];
        serverlist_info_t *info = &entry->info;
+
        // update description strings for engine menu and console output
-       dpsnprintf(entry->line1, sizeof(serverlist_cache[n].line1), "^%c%5d^7 ^%c%3u^7/%3u %-65.65s", info->ping >= 300 ? '1' : (info->ping >= 200 ? '3' : '7'), (int)info->ping, ((info->numhumans > 0 && info->numhumans < info->maxplayers) ? (info->numhumans >= 4 ? '7' : '3') : '1'), info->numplayers, info->maxplayers, info->name);
-       dpsnprintf(entry->line2, sizeof(serverlist_cache[n].line2), "^4%-21.21s %-19.19s ^%c%-17.17s^4 %-20.20s", info->cname, info->game,
+       entry->line1_len = dpsnprintf(entry->line1, sizeof(serverlist_cache[n].line1), "^%c%5.0f^7 ^%c%3u^7/%3u %-65.65s",
+                  info->ping >= 300 ? '1' : (info->ping >= 200 ? '3' : '7'),
+                  info->ping ? info->ping : INFINITY, // display inf when a listed server times out and net_slist_pause blocks its removal
+                  ((info->numhumans > 0 && info->numhumans < info->maxplayers) ? (info->numhumans >= 4 ? '7' : '3') : '1'),
+                  info->numplayers,
+                  info->maxplayers,
+                  info->name);
+       entry->line2_len = dpsnprintf(entry->line2, sizeof(serverlist_cache[n].line2), "^4%-21.21s %-19.19s ^%c%-17.17s^4 %-20.20s", info->cname, info->game,
                        (
                         info->gameversion != gameversion.integer
                         &&
@@ -1656,38 +1804,34 @@ static void NetConn_ClientParsePacket_ServerList_UpdateCache(int n)
                          )
                        ) ? '1' : '4',
                        info->mod, info->map);
-       if (entry->query == SQS_QUERIED)
+
+       if(!net_slist_pause.integer)
        {
-               if(!serverlist_paused)
-                       ServerList_ViewList_Remove(entry);
+               ServerList_ViewList_Remove(entry);
+               ServerList_ViewList_Insert(entry);
        }
-       // if not in the slist menu we should print the server to console (if wanted)
-       else if( serverlist_consoleoutput )
-               Con_Printf("%s\n%s\n", serverlist_cache[n].line1, serverlist_cache[n].line2);
-       // and finally, update the view set
-       if(!serverlist_paused)
-               ServerList_ViewList_Insert( entry );
-       //      update the entry's state
-       serverlist_cache[n].query = SQS_QUERIED;
+
+       if (serverlist_consoleoutput)
+               Con_Printf("%s\n%s\n", entry->line1, entry->line2);
 }
 
 // returns true, if it's sensible to continue the processing
-static qbool NetConn_ClientParsePacket_ServerList_PrepareQuery( int protocol, const char *ipstring, qbool isfavorite ) {
-       int n;
+static qbool NetConn_ClientParsePacket_ServerList_PrepareQuery(int protocol, const char *ipstring, qbool isfavorite)
+{
+       unsigned n;
        serverlist_entry_t *entry;
 
-       //      ignore the rest of the message if the serverlist is full
-       if( serverlist_cachecount == SERVERLIST_TOTALSIZE )
+       // ignore the rest of the message if the serverlist is full
+       if (serverlist_cachecount == SERVERLIST_TOTALSIZE)
                return false;
-       //      also ignore     it      if      we      have already queried    it      (other master server    response)
-       for( n =        0 ; n   < serverlist_cachecount ; n++   )
-               if( !strcmp( ipstring, serverlist_cache[ n ].info.cname ) )
+
+       for (n = 0; n < serverlist_cachecount; ++n)
+               if (!strcmp(ipstring, serverlist_cache[n].info.cname))
                        break;
 
-       if( n < serverlist_cachecount ) {
-               // the entry has already been queried once or 
+       // also ignore it if we have already queried it (other master server response)
+       if (n < serverlist_cachecount)
                return true;
-       }
 
        if (serverlist_maxcachecount <= n)
        {
@@ -1696,30 +1840,38 @@ static qbool NetConn_ClientParsePacket_ServerList_PrepareQuery( int protocol, co
        }
 
        entry = &serverlist_cache[n];
-
        memset(entry, 0, sizeof(*entry));
-       entry->protocol =       protocol;
-       //      store   the data        the engine cares about (address and     ping)
-       strlcpy (entry->info.cname, ipstring, sizeof(entry->info.cname));
-
+       entry->protocol = protocol;
+       entry->info.cname_len = dp_strlcpy(entry->info.cname, ipstring, sizeof(entry->info.cname));
        entry->info.isfavorite = isfavorite;
-       
-       // no, then reset the ping right away
-       entry->info.ping = -1;
-       // we also want to increase the serverlist_cachecount then
+
        serverlist_cachecount++;
        serverquerycount++;
 
-       entry->query =  SQS_QUERYING;
-
        return true;
 }
 
-static void NetConn_ClientParsePacket_ServerList_ParseDPList(lhnetaddress_t *senderaddress, const unsigned char *data, int length, qbool isextended)
+static void NetConn_ClientParsePacket_ServerList_ParseDPList(lhnetaddress_t *masteraddress, const char *masteraddressstring, const unsigned char *data, int length, qbool isextended)
 {
+       unsigned masternum;
+       lhnetaddress_t testaddress;
+
+       for (masternum = 0; masternum < DPMASTER_COUNT; ++masternum)
+               if (sv_masters[masternum].string[0]
+               && LHNETADDRESS_FromString(&testaddress, sv_masters[masternum].string, DPMASTER_PORT)
+               && LHNETADDRESS_Compare(&testaddress, masteraddress) == 0)
+                       break;
+       if (net_sourceaddresscheck.integer && masternum >= DPMASTER_COUNT)
+       {
+               Con_Printf(CON_WARN "ignoring DarkPlaces %sserver list from unrecognised master %s\n", isextended ? "extended " : "", masteraddressstring);
+               return;
+       }
+
        masterreplycount++;
-       if (serverlist_consoleoutput)
-               Con_Printf("received DarkPlaces %sserver list...\n", isextended ? "extended " : "");
+       dpmasterstatus[masternum] = MASTER_RX_RESPONSE;
+       if (serverlist_consoleoutput || net_slist_debug.integer)
+               Con_Printf("^5Received DarkPlaces server list %sfrom %s\n", isextended ? "(extended) " : "", sv_masters[masternum].string);
+
        while (length >= 7)
        {
                char ipstring [128];
@@ -1731,6 +1883,13 @@ static void NetConn_ClientParsePacket_ServerList_ParseDPList(lhnetaddress_t *sen
 
                        if (port != 0 && (data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF || data[4] != 0xFF))
                                dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%hu", data[1], data[2], data[3], data[4], port);
+                       else if (port == 0 && data[1] == 'E' && data[2] == 'O' && data[3] == 'T' && data[4] == '\0')
+                       {
+                               dpmasterstatus[masternum] = MASTER_RX_COMPLETE;
+                               if (net_slist_debug.integer)
+                                       Con_Printf("^4End Of Transmission %sfrom %s\n", isextended ? "(extended) " : "", sv_masters[masternum].string);
+                               break;
+                       }
 
                        // move on to next address in packet
                        data += 7;
@@ -1773,22 +1932,70 @@ static void NetConn_ClientParsePacket_ServerList_ParseDPList(lhnetaddress_t *sen
                }
                else
                {
-                       Con_Print("Error while parsing the server list\n");
+                       Con_Print(CON_WARN "Error while parsing the server list\n");
                        break;
                }
 
                if (serverlist_consoleoutput && developer_networking.integer)
                        Con_Printf("Requesting info from DarkPlaces server %s\n", ipstring);
-               
-               if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, ipstring, false ) ) {
-                       break;
-               }
 
+               if (!NetConn_ClientParsePacket_ServerList_PrepareQuery(PROTOCOL_DARKPLACES7, ipstring, false))
+                       break;
        }
 
+       if (serverlist_querystage & SLIST_QUERYSTAGE_QWMASTERS)
+               return; // we must wait if we're also querying QW as it has no EOT marker
        // begin or resume serverlist queries
-       serverlist_querysleep = false;
-       serverlist_querywaittime = host.realtime + 3;
+       for (masternum = 0; masternum < DPMASTER_COUNT; ++masternum)
+               if (dpmasterstatus[masternum] && dpmasterstatus[masternum] < MASTER_RX_COMPLETE)
+                       break; // was queried but no EOT marker received yet
+       if (masternum >= DPMASTER_COUNT)
+       {
+               serverlist_querystage = SLIST_QUERYSTAGE_SERVERS;
+               if (net_slist_debug.integer)
+                       Con_Print("^2Starting to query servers early (got EOT from all masters)\n");
+       }
+}
+
+static void NetConn_ClientParsePacket_ServerList_ParseQWList(lhnetaddress_t *masteraddress, const char *masteraddressstring, const unsigned char *data, int length)
+{
+       uint8_t masternum;
+       lhnetaddress_t testaddress;
+
+       for (masternum = 0; masternum < QWMASTER_COUNT; ++masternum)
+               if (sv_qwmasters[masternum].string[0]
+               && LHNETADDRESS_FromString(&testaddress, sv_qwmasters[masternum].string, QWMASTER_PORT)
+               && LHNETADDRESS_Compare(&testaddress, masteraddress) == 0)
+                       break;
+       if (net_sourceaddresscheck.integer && masternum >= QWMASTER_COUNT)
+       {
+               Con_Printf(CON_WARN "ignoring QuakeWorld server list from unrecognised master %s\n", masteraddressstring);
+               return;
+       }
+
+       masterreplycount++;
+       qwmasterstatus[masternum] = MASTER_RX_RESPONSE;
+       if (serverlist_consoleoutput || net_slist_debug.integer)
+               Con_Printf("^5Received QuakeWorld server list from %s\n", sv_qwmasters[masternum].string);
+
+       while (length >= 6 && (data[0] != 0xFF || data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF) && data[4] * 256 + data[5] != 0)
+       {
+               char ipstring[32];
+
+               dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%u", data[0], data[1], data[2], data[3], data[4] * 256 + data[5]);
+               if (serverlist_consoleoutput && developer_networking.integer)
+                       Con_Printf("Requesting info from QuakeWorld server %s\n", ipstring);
+
+               if (!NetConn_ClientParsePacket_ServerList_PrepareQuery(PROTOCOL_QUAKEWORLD, ipstring, false))
+                       break;
+
+               // move on to next address in packet
+               data += 6;
+               length -= 6;
+       }
+
+       // Unlike in NetConn_ClientParsePacket_ServerList_ParseDPList()
+       // we can't start to query servers early here because QW has no EOT marker.
 }
 #endif
 
@@ -1802,8 +2009,6 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
        size_t sendlength;
 #ifdef CONFIG_MENU
        char infostringvalue[MAX_INPUTLINE];
-       char ipstring[32];
-       const char *s;
 #endif
 
        // quakeworld ingame packet
@@ -1830,7 +2035,7 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                }
 
                sendlength = sizeof(senddata) - 4;
-               switch(Crypto_ClientParsePacket(string, length, senddata+4, &sendlength, peeraddress))
+               switch(Crypto_ClientParsePacket(string, length, senddata+4, &sendlength, peeraddress, addressstring2))
                {
                        case CRYPTO_NOMATCH:
                                // nothing to do
@@ -1883,7 +2088,7 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                                {
                                        int k;
                                        buf[45] = ' ';
-                                       strlcpy(buf + 46, argbuf, sizeof(buf) - 46);
+                                       dp_strlcpy(buf + 46, argbuf, sizeof(buf) - 46);
                                        NetConn_Write(mysocket, buf, 46 + (int)strlen(buf + 46), peeraddress);
                                        cls.rcon_commands[i][0] = 0;
                                        --cls.rcon_trying;
@@ -1911,15 +2116,15 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                {
                        // darkplaces or quake3
                        char protocolnames[1400];
-                       Con_DPrintf("\"%s\" received, sending connect request back to %s\n", string, addressstring2);
+
                        if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
-                               Con_DPrintf("challenge message from wrong server %s\n", addressstring2);
+                               Con_Printf(CON_WARN "ignoring challenge message from wrong server %s\n", addressstring2);
                                return true;
                        }
+                       Con_DPrintf("\"%s\" received, sending connect request back to %s\n", string, addressstring2);
+                       dp_strlcpy(cl_connect_status, "Connect: replying to challenge...", sizeof(cl_connect_status));
+
                        Protocol_Names(protocolnames, sizeof(protocolnames));
-#ifdef CONFIG_MENU
-                       M_Update_Return_Reason("Got challenge response");
-#endif
                        // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
                        InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
                        // TODO: add userinfo stuff here instead of using NQ commands?
@@ -1932,30 +2137,23 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                {
                        // darkplaces or quake3
                        if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
-                               Con_DPrintf("accept message from wrong server %s\n", addressstring2);
+                               Con_Printf(CON_WARN "ignoring accept message from wrong server %s\n", addressstring2);
                                return true;
                        }
-#ifdef CONFIG_MENU
-                       M_Update_Return_Reason("Accepted");
-#endif
                        NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_DARKPLACES3);
                        return true;
                }
                if (length > 7 && !memcmp(string, "reject ", 7) && cls.connect_trying)
                {
-                       char rejectreason[128];
                        if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
-                               Con_DPrintf("reject message from wrong server %s\n", addressstring2);
+                               Con_Printf(CON_WARN "ignoring reject message from wrong server %s\n", addressstring2);
                                return true;
                        }
                        cls.connect_trying = false;
                        string += 7;
-                       length = min(length - 7, (int)sizeof(rejectreason) - 1);
-                       memcpy(rejectreason, string, length);
-                       rejectreason[length] = 0;
-#ifdef CONFIG_MENU
-                       M_Update_Return_Reason(rejectreason);
-#endif
+                       length = min(length - 7, (int)sizeof(cl_connect_status) - 1);
+                       dpsnprintf(cl_connect_status, sizeof(cl_connect_status), "Connect: rejected, %.*s", length, string);
+                       Con_Printf(CON_ERROR "Connect: rejected by %s\n" CON_ERROR "%.*s\n", addressstring2, length, string);
                        return true;
                }
 #ifdef CONFIG_MENU
@@ -1969,43 +2167,35 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
 
                                string += 15;
                                // search the cache for this server and update it
-                               n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
+                               // the challenge is (ab)used to return the query time
+                               InfoString_GetValue(string, "challenge", infostringvalue, sizeof(infostringvalue));
+                               n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2, infostringvalue);
                                if (n < 0)
                                        return true;
 
                                info = &serverlist_cache[n].info;
-                               info->game[0] = 0;
-                               info->mod[0]  = 0;
-                               info->map[0]  = 0;
-                               info->name[0] = 0;
-                               info->qcstatus[0] = 0;
-                               info->players[0] = 0;
-                               info->protocol = -1;
-                               info->numplayers = 0;
-                               info->numbots = -1;
-                               info->maxplayers  = 0;
-                               info->gameversion = 0;
-
                                p = strchr(string, '\n');
                                if(p)
                                {
                                        *p = 0; // cut off the string there
                                        ++p;
+                                       info->players_len = dp_strlcpy(info->players, p, sizeof(info->players));
                                }
                                else
+                               {
                                        Con_Printf("statusResponse without players block?\n");
-
-                               if ((s = InfoString_GetValue(string, "gamename"     , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
-                               if ((s = InfoString_GetValue(string, "modname"      , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
-                               if ((s = InfoString_GetValue(string, "mapname"      , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
-                               if ((s = InfoString_GetValue(string, "hostname"     , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
-                               if ((s = InfoString_GetValue(string, "protocol"     , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
-                               if ((s = InfoString_GetValue(string, "clients"      , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
-                               if ((s = InfoString_GetValue(string, "bots"         , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
-                               if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
-                               if ((s = InfoString_GetValue(string, "gameversion"  , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
-                               if ((s = InfoString_GetValue(string, "qcstatus"     , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
-                               if (p                                                                                         != NULL) strlcpy(info->players, p, sizeof(info->players));
+                                       info->players_len = info->players[0] = 0;
+                               }
+                               info->game_len     = InfoString_GetValue(string, "gamename", info->game,     sizeof(info->game));
+                               info->mod_len      = InfoString_GetValue(string, "modname",  info->mod,      sizeof(info->mod));
+                               info->map_len      = InfoString_GetValue(string, "mapname",  info->map,      sizeof(info->map));
+                               info->name_len     = InfoString_GetValue(string, "hostname", info->name,     sizeof(info->name));
+                               info->qcstatus_len = InfoString_GetValue(string, "qcstatus", info->qcstatus, sizeof(info->qcstatus));
+                               info->protocol    = InfoString_GetValue(string, "protocol"     , infostringvalue, sizeof(infostringvalue)) ? atoi(infostringvalue) : -1;
+                               info->numplayers  = InfoString_GetValue(string, "clients"      , infostringvalue, sizeof(infostringvalue)) ? atoi(infostringvalue) : 0;
+                               info->numbots     = InfoString_GetValue(string, "bots"         , infostringvalue, sizeof(infostringvalue)) ? atoi(infostringvalue) : -1;
+                               info->maxplayers  = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue)) ? atoi(infostringvalue) : 0;
+                               info->gameversion = InfoString_GetValue(string, "gameversion"  , infostringvalue, sizeof(infostringvalue)) ? atoi(infostringvalue) : 0;
                                info->numhumans = info->numplayers - max(0, info->numbots);
                                info->freeslots = info->maxplayers - info->numplayers;
 
@@ -2020,33 +2210,24 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
 
                                string += 13;
                                // search the cache for this server and update it
-                               n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
+                               // the challenge is (ab)used to return the query time
+                               InfoString_GetValue(string, "challenge", infostringvalue, sizeof(infostringvalue));
+                               n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2, infostringvalue);
                                if (n < 0)
                                        return true;
 
                                info = &serverlist_cache[n].info;
-                               info->game[0] = 0;
-                               info->mod[0]  = 0;
-                               info->map[0]  = 0;
-                               info->name[0] = 0;
-                               info->qcstatus[0] = 0;
-                               info->players[0] = 0;
-                               info->protocol = -1;
-                               info->numplayers = 0;
-                               info->numbots = -1;
-                               info->maxplayers  = 0;
-                               info->gameversion = 0;
-
-                               if ((s = InfoString_GetValue(string, "gamename"     , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
-                               if ((s = InfoString_GetValue(string, "modname"      , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
-                               if ((s = InfoString_GetValue(string, "mapname"      , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
-                               if ((s = InfoString_GetValue(string, "hostname"     , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
-                               if ((s = InfoString_GetValue(string, "protocol"     , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
-                               if ((s = InfoString_GetValue(string, "clients"      , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
-                               if ((s = InfoString_GetValue(string, "bots"         , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
-                               if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
-                               if ((s = InfoString_GetValue(string, "gameversion"  , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
-                               if ((s = InfoString_GetValue(string, "qcstatus"     , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
+                               info->players_len = info->players[0] = 0;
+                               info->game_len     = InfoString_GetValue(string, "gamename", info->game,     sizeof(info->game));
+                               info->mod_len      = InfoString_GetValue(string, "modname",  info->mod,      sizeof(info->mod));
+                               info->map_len      = InfoString_GetValue(string, "mapname",  info->map,      sizeof(info->map));
+                               info->name_len     = InfoString_GetValue(string, "hostname", info->name,     sizeof(info->name));
+                               info->qcstatus_len = InfoString_GetValue(string, "qcstatus", info->qcstatus, sizeof(info->qcstatus));
+                               info->protocol    = InfoString_GetValue(string, "protocol"     , infostringvalue, sizeof(infostringvalue)) ? atoi(infostringvalue) : -1;
+                               info->numplayers  = InfoString_GetValue(string, "clients"      , infostringvalue, sizeof(infostringvalue)) ? atoi(infostringvalue) : 0;
+                               info->numbots     = InfoString_GetValue(string, "bots"         , infostringvalue, sizeof(infostringvalue)) ? atoi(infostringvalue) : -1;
+                               info->maxplayers  = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue)) ? atoi(infostringvalue) : 0;
+                               info->gameversion = InfoString_GetValue(string, "gameversion"  , infostringvalue, sizeof(infostringvalue)) ? atoi(infostringvalue) : 0;
                                info->numhumans = info->numplayers - max(0, info->numbots);
                                info->freeslots = info->maxplayers - info->numplayers;
 
@@ -2059,7 +2240,7 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                                // Extract the IP addresses
                                data += 18;
                                length -= 18;
-                               NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, false);
+                               NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, addressstring2, data, length, false);
                                return true;
                        }
                        if (!strncmp(string, "getserversExtResponse", 21) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
@@ -2067,7 +2248,7 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                                // Extract the IP addresses
                                data += 21;
                                length -= 21;
-                               NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, true);
+                               NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, addressstring2, data, length, true);
                                return true;
                        }
                        if (!memcmp(string, "d\n", 2) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
@@ -2075,26 +2256,7 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                                // Extract the IP addresses
                                data += 2;
                                length -= 2;
-                               masterreplycount++;
-                               if (serverlist_consoleoutput)
-                                       Con_Printf("received QuakeWorld server list from %s...\n", addressstring2);
-                               while (length >= 6 && (data[0] != 0xFF || data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF) && data[4] * 256 + data[5] != 0)
-                               {
-                                       dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%u", data[0], data[1], data[2], data[3], data[4] * 256 + data[5]);
-                                       if (serverlist_consoleoutput && developer_networking.integer)
-                                               Con_Printf("Requesting info from QuakeWorld server %s\n", ipstring);
-                                       
-                                       if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, ipstring, false ) ) {
-                                               break;
-                                       }
-
-                                       // move on to next address in packet
-                                       data += 6;
-                                       length -= 6;
-                               }
-                               // begin or resume serverlist queries
-                               serverlist_querysleep = false;
-                               serverlist_querywaittime = host.realtime + 3;
+                               NetConn_ClientParsePacket_ServerList_ParseQWList(peeraddress, addressstring2, data, length);
                                return true;
                        }
                }
@@ -2122,13 +2284,12 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                {
                        // challenge message
                        if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
-                               Con_DPrintf("c message from wrong server %s\n", addressstring2);
+                               Con_Printf(CON_WARN "ignoring c message from wrong server %s\n", addressstring2);
                                return true;
                        }
-                       Con_Printf("challenge %s received, sending QuakeWorld connect request back to %s\n", string + 1, addressstring2);
-#ifdef CONFIG_MENU
-                       M_Update_Return_Reason("Got QuakeWorld challenge response");
-#endif
+                       Con_DPrintf("challenge %s received, sending QuakeWorld connect request back to %s\n", string + 1, addressstring2);
+                       dp_strlcpy(cl_connect_status, "Connect: replying to challenge...", sizeof(cl_connect_status));
+
                        cls.qw_qport = qport.integer;
                        // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
                        InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
@@ -2141,12 +2302,9 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                {
                        // accept message
                        if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
-                               Con_DPrintf("j message from wrong server %s\n", addressstring2);
+                               Con_Printf(CON_WARN "ignoring j message from wrong server %s\n", addressstring2);
                                return true;
                        }
-#ifdef CONFIG_MENU
-                       M_Update_Return_Reason("QuakeWorld Accepted");
-#endif
                        NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_QUAKEWORLD);
                        return true;
                }
@@ -2155,6 +2313,7 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
 #ifdef CONFIG_MENU
                        serverlist_info_t *info;
                        int n;
+                       const char *s;
 
                        // qw server status
                        if (serverlist_consoleoutput && developer_networking.integer >= 2)
@@ -2162,20 +2321,20 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
 
                        string += 1;
                        // search the cache for this server and update it
-                       n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
+                       n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2, NULL);
                        if (n < 0)
                                return true;
 
                        info = &serverlist_cache[n].info;
-                       strlcpy(info->game, "QuakeWorld", sizeof(info->game));
-                       if ((s = InfoString_GetValue(string, "*gamedir"     , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));else info->mod[0]  = 0;
-                       if ((s = InfoString_GetValue(string, "map"          , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));else info->map[0]  = 0;
-                       if ((s = InfoString_GetValue(string, "hostname"     , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));else info->name[0] = 0;
+                       dp_strlcpy(info->game, "QuakeWorld", sizeof(info->game));
+                       info->mod_len  = InfoString_GetValue(string, "*gamedir", info->mod, sizeof(info->mod));
+                       info->map_len  = InfoString_GetValue(string, "map"     , info->map, sizeof(info->map));
+                       info->name_len = InfoString_GetValue(string, "hostname", info->name, sizeof(info->name));
                        info->protocol = 0;
                        info->numplayers = 0; // updated below
                        info->numhumans = 0; // updated below
-                       if ((s = InfoString_GetValue(string, "maxclients"   , infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);else info->maxplayers  = 0;
-                       if ((s = InfoString_GetValue(string, "gameversion"  , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);else info->gameversion = 0;
+                       info->maxplayers  = InfoString_GetValue(string, "maxclients" , infostringvalue, sizeof(infostringvalue)) ? atoi(infostringvalue) : 0;
+                       info->gameversion = InfoString_GetValue(string, "gameversion", infostringvalue, sizeof(infostringvalue)) ? atoi(infostringvalue) : 0;
 
                        // count active players on server
                        // (we could gather more info, but we're just after the number)
@@ -2203,7 +2362,7 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                {
                        // qw print command, used by rcon replies too
                        if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address) && LHNETADDRESS_Compare(peeraddress, &cls.rcon_address)) {
-                               Con_DPrintf("n message from wrong server %s\n", addressstring2);
+                               Con_Printf(CON_WARN "ignoring n message from wrong server %s\n", addressstring2);
                                return true;
                        }
                        Con_Printf("QW print command from server at %s:\n%s\n", addressstring2, string + 1);
@@ -2242,7 +2401,7 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        {
                                lhnetaddress_t clientportaddress;
                                if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
-                                       Con_DPrintf("CCREP_ACCEPT message from wrong server %s\n", addressstring2);
+                                       Con_Printf(CON_WARN "ignoring CCREP_ACCEPT message from wrong server %s\n", addressstring2);
                                        break;
                                }
                                clientportaddress = *peeraddress;
@@ -2264,23 +2423,18 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                                        Con_Printf("Connected to ProQuake %.1f server, enabling precise aim\n", cls.proquake_serverversion / 10.0f);
                                // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
                                InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
-#ifdef CONFIG_MENU
-                               M_Update_Return_Reason("Accepted");
-#endif
                                NetConn_ConnectionEstablished(mysocket, &clientportaddress, PROTOCOL_QUAKE);
                        }
                        break;
                case CCREP_REJECT:
-                       if (developer_extra.integer) {
-                               Con_DPrintf("CCREP_REJECT message from wrong server %s\n", addressstring2);
-                               break;
-                       }
                        if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address))
+                       {
+                               Con_Printf(CON_WARN "ignoring CCREP_REJECT message from wrong server %s\n", addressstring2);
                                break;
+                       }
                        cls.connect_trying = false;
-#ifdef CONFIG_MENU
-                       M_Update_Return_Reason((char *)MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
-#endif
+                       dpsnprintf(cl_connect_status, sizeof(cl_connect_status), "Connect: rejected, %s", MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
+                       Con_Printf(CON_ERROR "Connect: rejected by %s\n%s\n", addressstring2, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
                        break;
                case CCREP_SERVER_INFO:
                        if (developer_extra.integer)
@@ -2290,15 +2444,15 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        // we just ignore it and keep the real address
                        MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
                        // search the cache for this server and update it
-                       n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
+                       n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2, NULL);
                        if (n < 0)
                                break;
 
                        info = &serverlist_cache[n].info;
-                       strlcpy(info->game, "Quake", sizeof(info->game));
-                       strlcpy(info->mod , "", sizeof(info->mod)); // mod name is not specified
-                       strlcpy(info->name, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->name));
-                       strlcpy(info->map , MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->map));
+                       info->game_len = dp_strlcpy(info->game, "Quake", sizeof(info->game));
+                       info->mod_len  = dp_strlcpy(info->mod, "", sizeof(info->mod)); // mod name is not specified
+                       info->name_len = dp_strlcpy(info->name, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->name));
+                       info->map_len  = dp_strlcpy(info->map, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->map));
                        info->numplayers = MSG_ReadByte(&cl_message);
                        info->maxplayers = MSG_ReadByte(&cl_message);
                        info->protocol = MSG_ReadByte(&cl_message);
@@ -2308,7 +2462,7 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        break;
                case CCREP_RCON: // RocketGuy: ProQuake rcon support
                        if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.rcon_address)) {
-                               Con_DPrintf("CCREP_RCON message from wrong server %s\n", addressstring2);
+                               Con_Printf(CON_WARN "ignoring CCREP_RCON message from wrong server %s\n", addressstring2);
                                break;
                        }
                        if (developer_extra.integer)
@@ -2343,116 +2497,157 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
 #ifdef CONFIG_MENU
 void NetConn_QueryQueueFrame(void)
 {
-       int index;
-       int queries;
-       int maxqueries;
-       double timeouttime;
+       unsigned index;
+       unsigned maxqueries;
+       char dpquery[53]; // theoretical max: 14+22+16+1
+       double currentrealtime;
        static double querycounter = 0;
+       static unsigned pass = 0, server = 0;
+       unsigned queriesperserver = bound(1, net_slist_maxtries.integer, 8);
 
-       if(!net_slist_pause.integer && serverlist_paused)
-               ServerList_RebuildViewList();
-       serverlist_paused = net_slist_pause.integer != 0;
-
-       if (serverlist_querysleep)
+       if (!serverlist_querystage)
                return;
 
+       // If the client stalls partway through a frame (test command: `alias a a;a`)
+       // for correct pings we need to know what host.realtime would be if it were updated now.
+       currentrealtime = host.realtime + (Sys_DirtyTime() - host.dirtytime);
+
        // apply a cool down time after master server replies,
        // to avoid messing up the ping times on the servers
-       if (serverlist_querywaittime > host.realtime)
-               return;
+       if (serverlist_querystage < SLIST_QUERYSTAGE_SERVERS)
+       {
+               if (currentrealtime < masterquerytime + net_slist_timeout.value)
+                       return;
+
+               // Report the masters that timed out or whose response was incomplete.
+               for (index = 0; index < DPMASTER_COUNT; ++index)
+                       if (dpmasterstatus[index] && dpmasterstatus[index] < MASTER_RX_COMPLETE)
+                               Con_Printf(CON_WARN "WARNING: dpmaster %s %s\n", sv_masters[index].string, dpmasterstatus[index] == MASTER_TX_QUERY ? "timed out" : "response incomplete");
+               for (index = 0; index < QWMASTER_COUNT; ++index)
+                       if (qwmasterstatus[index] && qwmasterstatus[index] < MASTER_RX_RESPONSE) // no EOT marker in QW lists
+                               Con_Printf(CON_WARN "WARNING: qwmaster %s timed out\n", sv_qwmasters[index].string);
+
+               serverlist_querystage = SLIST_QUERYSTAGE_SERVERS;
+       }
 
        // each time querycounter reaches 1.0 issue a query
        querycounter += cl.realframetime * net_slist_queriespersecond.value;
-       maxqueries = (int)querycounter;
-       maxqueries = bound(0, maxqueries, net_slist_queriesperframe.integer);
+       maxqueries = bound(0, (int)querycounter, net_slist_queriesperframe.integer);
        querycounter -= maxqueries;
-
-       if( maxqueries == 0 ) {
+       if (maxqueries == 0)
                return;
-       }
 
-       //      scan serverlist and issue queries as needed
-       serverlist_querysleep = true;
-
-       timeouttime     = host.realtime - net_slist_timeout.value;
-       for( index = 0, queries = 0 ;   index   < serverlist_cachecount &&      queries < maxqueries    ; index++ )
+       if (pass < queriesperserver)
        {
-               serverlist_entry_t *entry = &serverlist_cache[ index ];
-               if( entry->query != SQS_QUERYING && entry->query != SQS_REFRESHING )
-               {
-                       continue;
-               }
+               // QW depends on waiting "long enough" between queries that responses "definitely" refer to the most recent querytime
+               // DP servers can echo back a timestamp for reliable (and more frequent, see net_slist_interval) pings
+               ServerList_BuildDPServerQuery(dpquery, sizeof(dpquery), currentrealtime);
 
-               serverlist_querysleep   = false;
-               if( entry->querycounter !=      0 && entry->querytime > timeouttime     )
+               for (unsigned queries = 0; server < serverlist_cachecount; ++server)
                {
-                       continue;
-               }
+                       lhnetaddress_t address;
+                       unsigned socket;
+                       serverlist_entry_t *entry = &serverlist_cache[server];
 
-               if( entry->querycounter !=      (unsigned) net_slist_maxtries.integer )
-               {
-                       lhnetaddress_t  address;
-                       int socket;
+                       if (queries >= maxqueries
+                       || currentrealtime <= entry->querytime + (entry->protocol == PROTOCOL_QUAKEWORLD ? net_slist_timeout : net_slist_interval).value)
+                               return; // continue this pass at the current server on a later frame
 
                        LHNETADDRESS_FromString(&address, entry->info.cname, 0);
-                       if      (entry->protocol == PROTOCOL_QUAKEWORLD)
+                       if (entry->protocol == PROTOCOL_QUAKEWORLD)
                        {
-                               for (socket     = 0; socket     < cl_numsockets ;       socket++)
-                                       NetConn_WriteString(cl_sockets[socket], "\377\377\377\377status\n", &address);
+                               for (socket = 0; socket < cl_numsockets; ++socket)
+                                       if (cl_sockets[socket])
+                                               NetConn_WriteString(cl_sockets[socket], "\377\377\377\377status\n", &address);
                        }
                        else
                        {
-                               for (socket     = 0; socket     < cl_numsockets ;       socket++)
-                                       NetConn_WriteString(cl_sockets[socket], "\377\377\377\377getstatus", &address);
+                               for (socket = 0; socket < cl_numsockets; ++socket)
+                                       if (cl_sockets[socket])
+                                               NetConn_WriteString(cl_sockets[socket], dpquery, &address);
                        }
 
-                       //      update the entry fields
-                       entry->querytime = host.realtime;
-                       entry->querycounter++;
+                       entry->querytime = currentrealtime;
+                       queries++;
 
-                       // if not in the slist menu we should print the server to console
                        if (serverlist_consoleoutput)
-                               Con_Printf("querying %25s (%i. try)\n", entry->info.cname, entry->querycounter);
-
-                       queries++;
+                               Con_Printf("querying %25s (%i. try)\n", entry->info.cname, pass + 1);
                }
-               else
+       }
+       else
+       {
+               // check timeouts
+               for (; server < serverlist_cachecount; ++server)
                {
-                       // have we tried to refresh this server?
-                       if( entry->query == SQS_REFRESHING ) {
-                               // yes, so update the reply count (since its not responding anymore)
-                               serverreplycount--;
-                               if(!serverlist_paused)
-                                       ServerList_ViewList_Remove(entry);
+                       serverlist_entry_t *entry = &serverlist_cache[server];
+
+                       if (!entry->responded // no acceptable response during this refresh cycle
+                       && entry->info.ping) // visible in the list (has old ping from previous refresh cycle)
+                       {
+                               if (currentrealtime > entry->querytime + net_slist_maxping.integer/1000)
+                               {
+                                       // you have no chance to survive make your timeout
+                                       serverreplycount--;
+                                       if(!net_slist_pause.integer)
+                                               ServerList_ViewList_Remove(entry);
+                                       entry->info.ping = 0; // removed later if net_slist_pause
+                               }
+                               else // still has time
+                                       return; // continue this pass at the current server on a later frame
                        }
-                       entry->query = SQS_TIMEDOUT;
                }
        }
+
+       // We finished the pass, ie didn't stop at maxqueries
+       // or a server that can't be (re)queried or timed out yet.
+       ++pass;
+       server = 0;
+
+       if (pass == queriesperserver)
+       {
+               // timeout pass begins next frame
+               if (net_slist_debug.integer)
+                       Con_Printf("^2Finished querying masters and servers in %f\n", currentrealtime - masterquerytime);
+       }
+       else if (pass > queriesperserver)
+       {
+               // Nothing else to do until the next refresh cycle.
+               if (net_slist_debug.integer)
+                       Con_Printf("^4Finished checking server timeouts in %f\n", currentrealtime - serverlist_cache[serverlist_cachecount - 1].querytime);
+               serverlist_querystage = 0;
+               pass = 0;
+       }
 }
 #endif
 
 void NetConn_ClientFrame(void)
 {
-       int i, length;
+       unsigned i;
+       int length;
        lhnetaddress_t peeraddress;
        unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
+
        NetConn_UpdateSockets();
+
        if (cls.connect_trying && cls.connect_nextsendtime < host.realtime)
        {
-#ifdef CONFIG_MENU
-               if (cls.connect_remainingtries == 0)
-                       M_Update_Return_Reason("Connect: Waiting 10 seconds for reply");
-#endif
-               cls.connect_nextsendtime = host.realtime + 1;
-               cls.connect_remainingtries--;
-               if (cls.connect_remainingtries <= -10)
+               if (cls.connect_remainingtries > 0)
                {
+                       cls.connect_remainingtries--;
+                       dpsnprintf(cl_connect_status, sizeof(cl_connect_status), "Connect: sending initial request, %i %s left...", cls.connect_remainingtries, cls.connect_remainingtries == 1 ? "retry" : "retries");
+               }
+               else
+               {
+                       char address[128];
+
                        cls.connect_trying = false;
-#ifdef CONFIG_MENU
-                       M_Update_Return_Reason("Connect: Failed");
-#endif
+                       LHNETADDRESS_ToString(&cls.connect_address, address, sizeof(address), true);
+                       dp_strlcpy(cl_connect_status, "Connect: failed, no reply", sizeof(cl_connect_status));
+                       Con_Printf(CON_ERROR "%s from %s\n", cl_connect_status, address);
                        return;
                }
+               cls.connect_nextsendtime = host.realtime + 1;
+
                // try challenge first (newer DP server or QW)
                NetConn_WriteString(cls.connect_mysocket, "\377\377\377\377getchallenge", &cls.connect_address);
                // then try netquake as a fallback (old server, or netquake)
@@ -2475,6 +2670,7 @@ void NetConn_ClientFrame(void)
                NetConn_Write(cls.connect_mysocket, cl_message.data, cl_message.cursize, &cls.connect_address);
                SZ_Clear(&cl_message);
        }
+
        for (i = 0;i < cl_numsockets;i++)
        {
                while (cl_sockets[i] && (length = NetConn_Read(cl_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
@@ -2623,17 +2819,17 @@ static qbool NetConn_BuildStatusResponse(const char* challenge, char* out_msg, s
                                if (IS_NEXUIZ_DERIVED(gamemode) && (teamplay.integer > 0))
                                {
                                        if(client->frags == -666) // spectator
-                                               strlcpy(teambuf, " 0", sizeof(teambuf));
+                                               dp_strlcpy(teambuf, " 0", sizeof(teambuf));
                                        else if(client->colors == 0x44) // red team
-                                               strlcpy(teambuf, " 1", sizeof(teambuf));
+                                               dp_strlcpy(teambuf, " 1", sizeof(teambuf));
                                        else if(client->colors == 0xDD) // blue team
-                                               strlcpy(teambuf, " 2", sizeof(teambuf));
+                                               dp_strlcpy(teambuf, " 2", sizeof(teambuf));
                                        else if(client->colors == 0xCC) // yellow team
-                                               strlcpy(teambuf, " 3", sizeof(teambuf));
+                                               dp_strlcpy(teambuf, " 3", sizeof(teambuf));
                                        else if(client->colors == 0x99) // pink team
-                                               strlcpy(teambuf, " 4", sizeof(teambuf));
+                                               dp_strlcpy(teambuf, " 4", sizeof(teambuf));
                                        else
-                                               strlcpy(teambuf, " 0", sizeof(teambuf));
+                                               dp_strlcpy(teambuf, " 0", sizeof(teambuf));
                                }
                                else
                                        *teambuf = 0;
@@ -2817,7 +3013,7 @@ static const char *RCon_Authenticate(lhnetaddress_t *peeraddress, const char *pa
        while((userpass_end = strchr(userpass_start, ' ')))
        {
                have_usernames = true;
-               strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
+               dp_ustr2stp(buf, sizeof(buf), userpass_start, userpass_end - userpass_start);
                if(buf[0])  // Ignore empty entries due to leading/duplicate space.
                        if(comparator(peeraddress, buf, password, cs, cslen))
                                goto allow;
@@ -2836,7 +3032,7 @@ static const char *RCon_Authenticate(lhnetaddress_t *peeraddress, const char *pa
        while((userpass_end = strchr(userpass_start, ' ')))
        {
                have_usernames = true;
-               strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
+               dp_ustr2stp(buf, sizeof(buf), userpass_start, userpass_end - userpass_start);
                if(buf[0])  // Ignore empty entries due to leading/duplicate space.
                        if(comparator(peeraddress, buf, password, cs, cslen))
                                goto check;
@@ -2926,7 +3122,7 @@ static void RCon_Execute(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, c
                        if(l)
                        {
                                client_t *host_client_save = host_client;
-                               Cmd_ExecuteString(cmd_local, s, src_local, true);
+                               Cmd_PreprocessAndExecuteString(cmd_local, s, l, src_local, true);
                                host_client = host_client_save;
                                // in case it is a command that changes host_client (like restart)
                        }
@@ -2936,7 +3132,10 @@ static void RCon_Execute(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, c
        }
        else
        {
-               Con_Printf("server denied rcon access to %s\n", host_client ? host_client->name : addressstring2);
+               if (!host_client || !host_client->netconnection || LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
+                       Con_Rcon_Redirect_Init(mysocket, peeraddress, proquakeprotocol);
+               Con_Printf(CON_ERROR "server denied rcon access to %s\n", host_client ? host_client->name : addressstring2);
+               Con_Rcon_Redirect_End();
        }
 }
 
@@ -3045,7 +3244,6 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                }
                if (length > 8 && !memcmp(string, "connect\\", 8))
                {
-                       char *s;
                        client_t *client;
                        crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
                        string += 7;
@@ -3070,12 +3268,12 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        }
                        else
                        {
-                               if ((s = InfoString_GetValue(string, "challenge", infostringvalue, sizeof(infostringvalue))))
+                               if (InfoString_GetValue(string, "challenge", infostringvalue, sizeof(infostringvalue)))
                                {
                                        // validate the challenge
                                        for (i = 0;i < MAX_CHALLENGES;i++)
                                                if(challenges[i].time > 0)
-                                                       if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strcmp(challenges[i].string, s))
+                                                       if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strcmp(challenges[i].string, infostringvalue))
                                                                break;
                                        // if the challenge is not recognized, drop the packet
                                        if (i == MAX_CHALLENGES)
@@ -3083,8 +3281,8 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                                }
                        }
 
-                       if((s = InfoString_GetValue(string, "message", infostringvalue, sizeof(infostringvalue))))
-                               Con_DPrintf("Connecting client %s sent us the message: %s\n", addressstring2, s);
+                       if(InfoString_GetValue(string, "message", infostringvalue, sizeof(infostringvalue)))
+                               Con_DPrintf("Connecting client %s sent us the message: %s\n", addressstring2, infostringvalue);
 
                        if(!(islocal || sv_public.integer > -2))
                        {
@@ -3097,7 +3295,7 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        }
 
                        // check engine protocol
-                       if(!(s = InfoString_GetValue(string, "protocol", infostringvalue, sizeof(infostringvalue))) || strcmp(s, "darkplaces 3"))
+                       if(!InfoString_GetValue(string, "protocol", infostringvalue, sizeof(infostringvalue)) || strcmp(infostringvalue, "darkplaces 3"))
                        {
                                if (developer_extra.integer)
                                        Con_Printf("Datagram_ParseConnectionless: sending \"reject Wrong game protocol.\" to %s.\n", addressstring2);
@@ -3429,7 +3627,7 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                                {
                                        // connect to the client
                                        // everything is allocated, just fill in the details
-                                       strlcpy (conn->address, addressstring2, sizeof (conn->address));
+                                       dp_strlcpy (conn->address, addressstring2, sizeof (conn->address));
                                        if (developer_extra.integer)
                                                Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_ACCEPT to %s.\n", addressstring2);
                                        // send back the info about the server connection
@@ -3482,7 +3680,7 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                                LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), myaddressstring, sizeof(myaddressstring), true);
                                MSG_WriteString(&sv_message, myaddressstring);
                                MSG_WriteString(&sv_message, hostname.string);
-                               MSG_WriteString(&sv_message, sv.name);
+                               MSG_WriteString(&sv_message, sv.worldbasename);
                                // How many clients are there?
                                for (i = 0, numclients = 0;i < svs.maxclients;i++)
                                        if (svs.clients[i].active)
@@ -3577,8 +3775,8 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                                char *s;
                                char *endpos;
                                const char *userlevel;
-                               strlcpy(password, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(password));
-                               strlcpy(cmd, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(cmd));
+                               dp_strlcpy(password, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(password));
+                               dp_strlcpy(cmd, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(cmd));
                                s = cmd;
                                endpos = cmd + strlen(cmd) + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
                                userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
@@ -3607,35 +3805,30 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
 
 void NetConn_ServerFrame(void)
 {
-       int i, length;
+       unsigned i;
+       int length;
        lhnetaddress_t peeraddress;
        unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
+
        for (i = 0;i < sv_numsockets;i++)
                while (sv_sockets[i] && (length = NetConn_Read(sv_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
                        NetConn_ServerParsePacket(sv_sockets[i], readbuffer, length, &peeraddress);
 }
 
-void NetConn_SleepMicroseconds(int microseconds)
-{
-       LHNET_SleepUntilPacket_Microseconds(microseconds);
-}
-
 #ifdef CONFIG_MENU
 void NetConn_QueryMasters(qbool querydp, qbool queryqw)
 {
-       int i, j;
-       int masternum;
+       unsigned i, j;
+       unsigned masternum;
        lhnetaddress_t masteraddress;
-       lhnetaddress_t broadcastaddress;
        char request[256];
+       char lookupstring[128];
 
        if (serverlist_cachecount >= SERVERLIST_TOTALSIZE)
                return;
 
-       // 26000 is the default quake server port, servers on other ports will not
-       // be found
-       // note this is IPv4-only, I doubt there are IPv6-only LANs out there
-       LHNETADDRESS_FromString(&broadcastaddress, "255.255.255.255", 26000);
+       memset(dpmasterstatus, 0, sizeof(*dpmasterstatus));
+       memset(qwmasterstatus, 0, sizeof(*qwmasterstatus));
 
        if (querydp)
        {
@@ -3644,24 +3837,7 @@ void NetConn_QueryMasters(qbool querydp, qbool queryqw)
                        if (cl_sockets[i])
                        {
                                const char *cmdname, *extraoptions;
-                               int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
-
-                               if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
-                               {
-                                       // search LAN for Quake servers
-                                       SZ_Clear(&cl_message);
-                                       // save space for the header, filled in later
-                                       MSG_WriteLong(&cl_message, 0);
-                                       MSG_WriteByte(&cl_message, CCREQ_SERVER_INFO);
-                                       MSG_WriteString(&cl_message, "QUAKE");
-                                       MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
-                                       StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
-                                       NetConn_Write(cl_sockets[i], cl_message.data, cl_message.cursize, &broadcastaddress);
-                                       SZ_Clear(&cl_message);
-
-                                       // search LAN for DarkPlaces servers
-                                       NetConn_WriteString(cl_sockets[i], "\377\377\377\377getstatus", &broadcastaddress);
-                               }
+                               lhnetaddresstype_t af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
 
                                // build the getservers message to send to the dpmaster master servers
                                if (LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == LHNETADDRESSTYPE_INET6)
@@ -3678,24 +3854,28 @@ void NetConn_QueryMasters(qbool querydp, qbool queryqw)
                                dpsnprintf(request+4, sizeof(request)-4, "%s %s %u empty full%s", cmdname, gamenetworkfiltername, NET_PROTOCOL_VERSION, extraoptions);
 
                                // search internet
-                               for (masternum = 0;sv_masters[masternum].name;masternum++)
+                               for (masternum = 0; masternum < DPMASTER_COUNT; ++masternum)
                                {
-                                       if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == af)
+                                       if(sv_masters[masternum].string[0]
+                                       && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT)
+                                       && LHNETADDRESS_GetAddressType(&masteraddress) == af)
                                        {
+                                               if (serverlist_consoleoutput || net_slist_debug.integer)
+                                               {
+                                                       LHNETADDRESS_ToString(&masteraddress, lookupstring, sizeof(lookupstring), true);
+                                                       Con_Printf("Querying DP master %s (resolved from %s)\n", lookupstring, sv_masters[masternum].string);
+                                               }
                                                masterquerycount++;
                                                NetConn_WriteString(cl_sockets[i], request, &masteraddress);
+                                               dpmasterstatus[masternum] = MASTER_TX_QUERY;
                                        }
                                }
 
                                // search favorite servers
                                for(j = 0; j < nFavorites; ++j)
-                               {
-                                       if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
-                                       {
-                                               if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
-                                                       NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, request, true );
-                                       }
-                               }
+                                       if(LHNETADDRESS_GetAddressType(&favorites[j]) == af
+                                       && LHNETADDRESS_ToString(&favorites[j], lookupstring, sizeof(lookupstring), true))
+                                               NetConn_ClientParsePacket_ServerList_PrepareQuery(PROTOCOL_DARKPLACES7, lookupstring, true);
                        }
                }
        }
@@ -3703,57 +3883,45 @@ void NetConn_QueryMasters(qbool querydp, qbool queryqw)
        // only query QuakeWorld servers when the user wants to
        if (queryqw)
        {
+               dpsnprintf(request, sizeof(request), "c\n");
+
                for (i = 0;i < cl_numsockets;i++)
                {
                        if (cl_sockets[i])
                        {
-                               int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
-
-                               if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
-                               {
-                                       // search LAN for QuakeWorld servers
-                                       NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &broadcastaddress);
-
-                                       // build the getservers message to send to the qwmaster master servers
-                                       // note this has no -1 prefix, and the trailing nul byte is sent
-                                       dpsnprintf(request, sizeof(request), "c\n");
-                               }
+                               lhnetaddresstype_t af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
 
                                // search internet
-                               for (masternum = 0;sv_qwmasters[masternum].name;masternum++)
+                               for (masternum = 0; masternum < QWMASTER_COUNT; ++masternum)
                                {
-                                       if (sv_qwmasters[masternum].string && LHNETADDRESS_FromString(&masteraddress, sv_qwmasters[masternum].string, QWMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])))
+                                       if(sv_qwmasters[masternum].string[0]
+                                       && LHNETADDRESS_FromString(&masteraddress, sv_qwmasters[masternum].string, QWMASTER_PORT)
+                                       && LHNETADDRESS_GetAddressType(&masteraddress) == af)
                                        {
-                                               if (m_state != m_slist)
+                                               if (serverlist_consoleoutput || net_slist_debug.integer)
                                                {
-                                                       char lookupstring[128];
                                                        LHNETADDRESS_ToString(&masteraddress, lookupstring, sizeof(lookupstring), true);
-                                                       Con_Printf("Querying master %s (resolved from %s)\n", lookupstring, sv_qwmasters[masternum].string);
+                                                       Con_Printf("Querying QW master %s (resolved from %s)\n", lookupstring, sv_qwmasters[masternum].string);
                                                }
                                                masterquerycount++;
                                                NetConn_Write(cl_sockets[i], request, (int)strlen(request) + 1, &masteraddress);
+                                               qwmasterstatus[masternum] = MASTER_TX_QUERY;
                                        }
                                }
 
                                // search favorite servers
                                for(j = 0; j < nFavorites; ++j)
-                               {
-                                       if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
-                                       {
-                                               if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
-                                               {
-                                                       NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &favorites[j]);
-                                                       NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, request, true );
-                                               }
-                                       }
-                               }
+                                       if(LHNETADDRESS_GetAddressType(&favorites[j]) == af
+                                       && LHNETADDRESS_ToString(&favorites[j], lookupstring, sizeof(lookupstring), true))
+                                               NetConn_ClientParsePacket_ServerList_PrepareQuery(PROTOCOL_QUAKEWORLD, lookupstring, true);
                        }
                }
        }
+
        if (!masterquerycount)
        {
                Con_Print(CON_ERROR "Unable to query master servers, no suitable network sockets active.\n");
-               M_Update_Return_Reason("No network");
+               dp_strlcpy(cl_connect_status, "No network", sizeof(cl_connect_status));
        }
 }
 #endif
@@ -3761,7 +3929,7 @@ void NetConn_QueryMasters(qbool querydp, qbool queryqw)
 void NetConn_Heartbeat(int priority)
 {
        lhnetaddress_t masteraddress;
-       int masternum;
+       uint8_t masternum;
        lhnetsocket_t *mysocket;
 
        // if it's a state change (client connected), limit next heartbeat to no
@@ -3783,8 +3951,10 @@ void NetConn_Heartbeat(int priority)
        if (sv.active && sv_public.integer > 0 && svs.maxclients >= 2 && (priority > 1 || host.realtime > nextheartbeattime))
        {
                nextheartbeattime = host.realtime + sv_heartbeatperiod.value;
-               for (masternum = 0;sv_masters[masternum].name;masternum++)
-                       if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && (mysocket = NetConn_ChooseServerSocketForAddress(&masteraddress)))
+               for (masternum = 0; masternum < DPMASTER_COUNT; ++masternum)
+                       if (sv_masters[masternum].string[0]
+                       && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT)
+                       && (mysocket = NetConn_ChooseServerSocketForAddress(&masteraddress)))
                                NetConn_WriteString(mysocket, "\377\377\377\377heartbeat DarkPlaces\x0A", &masteraddress);
        }
 }
@@ -3825,11 +3995,13 @@ void Net_Stats_f(cmd_state_t *cmd)
 #ifdef CONFIG_MENU
 void Net_Refresh_f(cmd_state_t *cmd)
 {
-       if (m_state != m_slist) {
-               Con_Print("Sending new requests to master servers\n");
+       if (m_state != m_slist)
+       {
+               Con_Print("Sending new requests to DP master servers\n");
                ServerList_QueryList(false, true, false, true);
                Con_Print("Listening for replies...\n");
-       } else
+       }
+       else
                ServerList_QueryList(false, true, false, false);
 }
 
@@ -3838,11 +4010,13 @@ void Net_Slist_f(cmd_state_t *cmd)
        ServerList_ResetMasks();
        serverlist_sortbyfield = SLIF_PING;
        serverlist_sortflags = 0;
-    if (m_state != m_slist) {
-               Con_Print("Sending requests to master servers\n");
+       if (m_state != m_slist)
+       {
+               Con_Print("Sending requests to DP master servers\n");
                ServerList_QueryList(true, true, false, true);
                Con_Print("Listening for replies...\n");
-       } else
+       }
+       else
                ServerList_QueryList(true, true, false, false);
 }
 
@@ -3851,12 +4025,13 @@ void Net_SlistQW_f(cmd_state_t *cmd)
        ServerList_ResetMasks();
        serverlist_sortbyfield = SLIF_PING;
        serverlist_sortflags = 0;
-    if (m_state != m_slist) {
-               Con_Print("Sending requests to master servers\n");
+       if (m_state != m_slist)
+       {
+               Con_Print("Sending requests to QW master servers\n");
                ServerList_QueryList(true, false, true, true);
-               serverlist_consoleoutput = true;
                Con_Print("Listening for replies...\n");
-       } else
+       }
+       else
                ServerList_QueryList(true, false, true, false);
 }
 #endif
@@ -3864,7 +4039,9 @@ void Net_SlistQW_f(cmd_state_t *cmd)
 void NetConn_Init(void)
 {
        int i;
+       unsigned j;
        lhnetaddress_t tempaddress;
+
        netconn_mempool = Mem_AllocPool("network connections", 0, NULL);
        Cmd_AddCommand(CF_SHARED, "net_stats", Net_Stats_f, "print network statistics");
 #ifdef CONFIG_MENU
@@ -3879,15 +4056,21 @@ void NetConn_Init(void)
        Cvar_RegisterVariable(&rcon_restricted_password);
        Cvar_RegisterVariable(&rcon_restricted_commands);
        Cvar_RegisterVariable(&rcon_secure_maxdiff);
+
+#ifdef CONFIG_MENU
+       Cvar_RegisterVariable(&net_slist_debug);
+       Cvar_RegisterVariable(&net_slist_favorites);
+       Cvar_RegisterCallback(&net_slist_favorites, NetConn_UpdateFavorites_c);
+       Cvar_RegisterVariable(&net_slist_interval);
+       Cvar_RegisterVariable(&net_slist_maxping);
+       Cvar_RegisterVariable(&net_slist_maxtries);
+       Cvar_RegisterVariable(&net_slist_pause);
+       Cvar_RegisterCallback(&net_slist_pause, ServerList_RebuildViewList);
        Cvar_RegisterVariable(&net_slist_queriespersecond);
        Cvar_RegisterVariable(&net_slist_queriesperframe);
        Cvar_RegisterVariable(&net_slist_timeout);
-       Cvar_RegisterVariable(&net_slist_maxtries);
-       Cvar_RegisterVariable(&net_slist_favorites);
-#ifdef CONFIG_MENU
-       Cvar_RegisterCallback(&net_slist_favorites, NetConn_UpdateFavorites_c);
 #endif
-       Cvar_RegisterVariable(&net_slist_pause);
+
 #ifdef IP_TOS // register cvar only if supported
        Cvar_RegisterVariable(&net_tos_dscp);
 #endif
@@ -3906,7 +4089,7 @@ void NetConn_Init(void)
        Cvar_RegisterVariable(&hostname);
        Cvar_RegisterVariable(&developer_networking);
        Cvar_RegisterVariable(&cl_netport);
-       Cvar_RegisterCallback(&cl_netport, NetConn_cl_netport_Callback);
+       Cvar_RegisterCallback(&cl_netport, NetConn_CL_UpdateSockets_Callback);
        Cvar_RegisterVariable(&sv_netport);
        Cvar_RegisterCallback(&sv_netport, NetConn_sv_netport_Callback);
        Cvar_RegisterVariable(&net_address);
@@ -3914,8 +4097,12 @@ void NetConn_Init(void)
        Cvar_RegisterVariable(&sv_public);
        Cvar_RegisterVariable(&sv_public_rejectreason);
        Cvar_RegisterVariable(&sv_heartbeatperiod);
-       for (i = 0;sv_masters[i].name;i++)
-               Cvar_RegisterVariable(&sv_masters[i]);
+       for (j = 0; j < DPMASTER_COUNT; ++j)
+               Cvar_RegisterVariable(&sv_masters[j]);
+#ifdef CONFIG_MENU
+       for (j = 0; j < QWMASTER_COUNT; ++j)
+               Cvar_RegisterVariable(&sv_qwmasters[j]);
+#endif
        Cvar_RegisterVariable(&gameversion);
        Cvar_RegisterVariable(&gameversion_min);
        Cvar_RegisterVariable(&gameversion_max);
index f8e9f7a459031b78188819a900b41801144c9474..a7ef91d22d59999492f7490d8004dd0c75b90db7 100755 (executable)
--- a/netconn.h
+++ b/netconn.h
@@ -274,20 +274,27 @@ typedef struct serverlist_info_s
 {
        /// address for connecting
        char cname[128];
-       /// ping time for sorting servers
-       int ping;
+       unsigned cname_len;
+       /// ping time for sorting servers, in milliseconds, 0 means no data
+       unsigned ping;
        /// name of the game
        char game[32];
+       unsigned game_len;
        /// name of the mod
        char mod[32];
+       unsigned mod_len;
        /// name of the map
        char map[32];
+       unsigned map_len;
        /// name of the session
        char name[128];
+       unsigned name_len;
        /// qc-defined short status string
        char qcstatus[128];
+       unsigned qcstatus_len;
        /// frags/ping/name list (if they fit in the packet)
        char players[2800];
+       unsigned players_len;
        /// max client number
        int maxplayers;
        /// number of currently connected players (including bots)
@@ -305,7 +312,7 @@ typedef struct serverlist_info_s
        ///  not filterable by QC)
        int gameversion;
 
-       // categorized sorting
+       /// categorized sorting
        int category;
        /// favorite server flag
        qbool isfavorite;
@@ -339,22 +346,11 @@ typedef enum
        SLSF_CATEGORIES = 4
 } serverlist_sortflags_t;
 
-typedef enum
-{
-       SQS_NONE = 0,
-       SQS_QUERYING,
-       SQS_QUERIED,
-       SQS_TIMEDOUT,
-       SQS_REFRESHING
-} serverlist_query_state;
-
 typedef struct serverlist_entry_s
 {
-       /// used to determine whether this entry should be included into the final view
-       serverlist_query_state query;
-       /// used to count the number of times the host has tried to query this server already
-       unsigned querycounter;
-       /// used to calculate ping when update comes in
+       /// used to track when a server should be considered timed out and removed from the final view
+       qbool responded;
+       /// used to calculate ping in PROTOCOL_QUAKEWORLD, and for net_slist_maxtries interval, and for timeouts
        double querytime;
        /// query protocol to use on this server, may be PROTOCOL_QUAKEWORLD or PROTOCOL_DARKPLACES7
        int protocol;
@@ -363,7 +359,9 @@ typedef struct serverlist_entry_s
 
        // legacy stuff
        char line1[128];
+       unsigned line1_len;
        char line2[128];
+       unsigned line2_len;
 } serverlist_entry_t;
 
 typedef struct serverlist_mask_s
@@ -380,21 +378,19 @@ extern serverlist_mask_t serverlist_andmasks[SERVERLIST_ANDMASKCOUNT];
 extern serverlist_mask_t serverlist_ormasks[SERVERLIST_ORMASKCOUNT];
 
 extern serverlist_infofield_t serverlist_sortbyfield;
-extern int serverlist_sortflags; // not using the enum, as it is a bitmask
+extern unsigned serverlist_sortflags; // not using the enum, as it is a bitmask
 
 #if SERVERLIST_TOTALSIZE > 65536
 #error too many servers, change type of index array
 #endif
-extern int serverlist_viewcount;
-extern unsigned short serverlist_viewlist[SERVERLIST_VIEWLISTSIZE];
+extern unsigned serverlist_viewcount;
+extern uint16_t serverlist_viewlist[SERVERLIST_VIEWLISTSIZE];
 
-extern int serverlist_cachecount;
+extern unsigned serverlist_cachecount;
 extern serverlist_entry_t *serverlist_cache;
 extern const serverlist_entry_t *serverlist_callbackentry;
 
-extern qbool serverlist_consoleoutput;
-
-void ServerList_GetPlayerStatistics(int *numplayerspointer, int *maxplayerspointer);
+void ServerList_GetPlayerStatistics(unsigned *numplayerspointer, unsigned *maxplayerspointer);
 #endif
 
 //============================================================================
@@ -404,19 +400,19 @@ void ServerList_GetPlayerStatistics(int *numplayerspointer, int *maxplayerspoint
 //============================================================================
 
 extern char cl_net_extresponse[NET_EXTRESPONSE_MAX][1400];
-extern int cl_net_extresponse_count;
-extern int cl_net_extresponse_last;
+extern unsigned cl_net_extresponse_count;
+extern unsigned cl_net_extresponse_last;
 
 extern char sv_net_extresponse[NET_EXTRESPONSE_MAX][1400];
-extern int sv_net_extresponse_count;
-extern int sv_net_extresponse_last;
+extern unsigned sv_net_extresponse_count;
+extern unsigned sv_net_extresponse_last;
 
 #ifdef CONFIG_MENU
 extern double masterquerytime;
-extern int masterquerycount;
-extern int masterreplycount;
-extern int serverquerycount;
-extern int serverreplycount;
+extern unsigned masterquerycount;
+extern unsigned masterreplycount;
+extern unsigned serverquerycount;
+extern unsigned serverreplycount;
 #endif
 
 extern sizebuf_t cl_message;
@@ -443,7 +439,6 @@ void NetConn_CloseClientPorts(void);
 void NetConn_OpenClientPorts(void);
 void NetConn_CloseServerPorts(void);
 void NetConn_OpenServerPorts(int opennetports);
-void NetConn_UpdateSockets_Client(void);
 void NetConn_UpdateSockets(void);
 lhnetsocket_t *NetConn_ChooseClientSocketForAddress(lhnetaddress_t *address);
 lhnetsocket_t *NetConn_ChooseServerSocketForAddress(lhnetaddress_t *address);
@@ -458,7 +453,6 @@ int NetConn_WriteString(lhnetsocket_t *mysocket, const char *string, const lhnet
 int NetConn_IsLocalGame(void);
 void NetConn_ClientFrame(void);
 void NetConn_ServerFrame(void);
-void NetConn_SleepMicroseconds(int microseconds);
 void NetConn_Heartbeat(int priority);
 void Net_Stats_f(struct cmd_state_s *cmd);
 
@@ -471,7 +465,7 @@ void Net_Refresh_f(struct cmd_state_s *cmd);
 
 /// ServerList interface (public)
 /// manually refresh the view set, do this after having changed the mask or any other flag
-void ServerList_RebuildViewList(void);
+void ServerList_RebuildViewList(cvar_t* var);
 void ServerList_ResetMasks(void);
 void ServerList_QueryList(qbool resetcache, qbool querydp, qbool queryqw, qbool consoleoutput);
 
index 8408f8fc5b1c99d651ae03d447b53b43449b57bc..4892fdc1d436c359c7be87bb8e5d1b5503cc1b0a 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>\r
 <packages>\r
-  <package id="sdl2.nuget" version="2.0.14" targetFramework="native" />\r
-  <package id="sdl2.nuget.redist" version="2.0.14" targetFramework="native" />\r
+  <package id="sdl2.nuget" version="2.30.2" targetFramework="native" />\r
+  <package id="sdl2.nuget.redist" version="2.30.2" targetFramework="native" />\r
 </packages>
\ No newline at end of file
diff --git a/phys.c b/phys.c
new file mode 100644 (file)
index 0000000..8d71b86
--- /dev/null
+++ b/phys.c
@@ -0,0 +1,80 @@
+// for physics functions shared by the client and server
+
+#include "phys.h"
+
+#include "quakedef.h"
+#include "cl_collision.h"
+
+
+int PHYS_NudgeOutOfSolid(prvm_prog_t *prog, prvm_edict_t *ent)
+{
+       int bump, pass;
+       trace_t stucktrace;
+       vec3_t testorigin, targetorigin;
+       vec3_t stuckmins, stuckmaxs;
+       vec_t separation;
+       model_t *worldmodel;
+
+       if (prog == SVVM_prog)
+       {
+               worldmodel = sv.worldmodel;
+               separation = sv_gameplayfix_nudgeoutofsolid_separation.value;
+       }
+       else if (prog == CLVM_prog)
+       {
+               worldmodel = cl.worldmodel;
+               separation = cl_gameplayfix_nudgeoutofsolid_separation.value;
+       }
+       else
+               Sys_Error("PHYS_NudgeOutOfSolid: cannot be called from %s VM\n", prog->name);
+
+       VectorCopy(PRVM_serveredictvector(ent, mins), stuckmins);
+       VectorCopy(PRVM_serveredictvector(ent, maxs), stuckmaxs);
+       if (worldmodel && worldmodel->brushq1.numclipnodes)
+               separation = 0.0f; // when using hulls, it can not be enlarged
+       else
+       {
+               stuckmins[0] -= separation;
+               stuckmins[1] -= separation;
+               stuckmins[2] -= separation;
+               stuckmaxs[0] += separation;
+               stuckmaxs[1] += separation;
+               stuckmaxs[2] += separation;
+       }
+
+       // first pass we try to get it out of brush entities
+       // second pass we try to get it out of world only (can't win them all)
+       for (pass = 0;pass < 2;pass++)
+       {
+               VectorCopy(PRVM_serveredictvector(ent, origin), testorigin);
+               for (bump = 0;bump < 10;bump++)
+               {
+                       if (prog == SVVM_prog) // TODO: can we refactor to use a shared TraceBox or at least a func ptr for these cases?
+                               stucktrace = SV_TraceBox(testorigin, stuckmins, stuckmaxs, testorigin, pass ? MOVE_WORLDONLY : MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value);
+                       else
+                               stucktrace = CL_TraceBox(testorigin, stuckmins, stuckmaxs, testorigin, pass ? MOVE_WORLDONLY : MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value, pass ? false : true, false, NULL, false);
+
+                       // Separation compared here to ensure a good location will be recognised reliably.
+                       if (-stucktrace.startdepth <= separation
+                       || (!stucktrace.bmodelstartsolid && !stucktrace.worldstartsolid)
+                       || (pass && !stucktrace.worldstartsolid))
+                       {
+                               // found a good location, use it
+                               VectorCopy(testorigin, PRVM_serveredictvector(ent, origin));
+                               return bump || pass ? 1 : -1; // -1 means it wasn't stuck
+                       }
+
+                       VectorMA(testorigin, -stucktrace.startdepth, stucktrace.startdepthnormal, targetorigin);
+                       // Trace to targetorigin so we don't set it out of the world in complex cases.
+                       if (prog == SVVM_prog)
+                               stucktrace = SV_TraceBox(testorigin, stuckmins, stuckmaxs, targetorigin, pass ? MOVE_WORLDONLY : MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value);
+                       else
+                               stucktrace = CL_TraceBox(testorigin, stuckmins, stuckmaxs, targetorigin, pass ? MOVE_WORLDONLY : MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value, pass ? false : true, false, NULL, false);
+                       if (stucktrace.fraction)
+                               VectorCopy(stucktrace.endpos, testorigin);
+                       else
+                               break; // Can't move it so no point doing more iterations on this pass.
+               }
+       }
+       return 0;
+}
diff --git a/phys.h b/phys.h
new file mode 100644 (file)
index 0000000..d18cfb9
--- /dev/null
+++ b/phys.h
@@ -0,0 +1,14 @@
+#ifndef PHYS_H
+#define PHYS_H
+
+#include "quakedef.h"
+
+
+/*! move an entity that is stuck out of the surface it is stuck in (can move large amounts)
+ * returns 1 if it found a better place, 0 if it remains stuck, -1 if it wasn't stuck.
+ */
+int PHYS_NudgeOutOfSolid(prvm_prog_t *prog, prvm_edict_t *ent);
+extern cvar_t cl_gameplayfix_nudgeoutofsolid_separation;
+
+
+#endif // PHYS_H guard
index 92ead5dc772a8393f7a7470fee8cec35be11a78c..32632f30f9799e100fe30abb693f4da95e5b0db0 100644 (file)
--- a/progsvm.h
+++ b/progsvm.h
@@ -233,7 +233,9 @@ extern prvm_eval_t prvm_badvalue;
 #endif
 
 //============================================================================
-#define PRVM_OP_STATE          1
+// prog->flag
+#define PRVM_OP_STATE       1
+#define PRVM_CSQC_SIMPLE    2
 
 #ifdef DP_SMALLMEMORY
 #define        PRVM_MAX_STACK_DEPTH            128
@@ -697,7 +699,7 @@ typedef struct prvm_prog_s
        const char                      *name; // [INIT]
 
        // flag - used to store general flags like PRVM_GE_SELF, etc.
-       int                             flag;
+       unsigned                        flag;
 
        const char                      **extensionstring; // [INIT]
 
@@ -717,7 +719,7 @@ typedef struct prvm_prog_s
 
 //     prvm_builtin_mem_t  *mem_list;
 
-// now passed as parameter of PRVM_LoadProgs
+// now passed as parameter of PRVM_Prog_Load
 //     char                            **required_func;
 //     int                                     numrequiredfunc;
 
@@ -822,7 +824,7 @@ void PRVM_CallProfile_f(struct cmd_state_s *cmd);
 void PRVM_PrintFunction_f(struct cmd_state_s *cmd);
 
 void PRVM_PrintState(prvm_prog_t *prog, int stack_index);
-void PRVM_Crash(prvm_prog_t *prog);
+void PRVM_Crash(void);
 void PRVM_ShortStackTrace(prvm_prog_t *prog, char *buf, size_t bufsize);
 const char *PRVM_AllocationOrigin(prvm_prog_t *prog);
 void PRVM_GarbageCollection(prvm_prog_t *prog);
@@ -901,7 +903,8 @@ void PRVM_ED_PrintNum (prvm_prog_t *prog, int ent, const char *wildcard_fieldnam
 const char *PRVM_GetString(prvm_prog_t *prog, int num);
 int PRVM_SetEngineString(prvm_prog_t *prog, const char *s);
 const char *PRVM_ChangeEngineString(prvm_prog_t *prog, int i, const char *s);
-int PRVM_SetTempString(prvm_prog_t *prog, const char *s);
+/// Takes an strlen (not a buffer size).
+int PRVM_SetTempString(prvm_prog_t *prog, const char *s, size_t slen);
 int PRVM_AllocString(prvm_prog_t *prog, size_t bufferlength, char **pointer);
 void PRVM_FreeString(prvm_prog_t *prog, int num);
 
@@ -921,7 +924,7 @@ Load a program with LoadProgs
 */
 // Load expects to be called right after Reset
 void PRVM_Prog_Init(prvm_prog_t *prog, struct cmd_state_s *cmd);
-void PRVM_Prog_Load(prvm_prog_t *prog, const char *filename, unsigned char *data, int64_t size, int numrequiredfunc, const char **required_func, int numrequiredfields, prvm_required_field_t *required_field, int numrequiredglobals, prvm_required_field_t *required_global);
+void PRVM_Prog_Load(prvm_prog_t *prog, const char *filename, unsigned char *data, fs_offset_t size, void CheckRequiredFuncs(prvm_prog_t *prog, const char *filename), int numrequiredfields, prvm_required_field_t *required_field, int numrequiredglobals, prvm_required_field_t *required_global);
 void PRVM_Prog_Reset(prvm_prog_t *prog);
 
 void PRVM_StackTrace(prvm_prog_t *prog);
index 5685675562e0e51096425f306fcbf741129f09f2..d72e961571fb698cda74276421b2da6609caf224 100644 (file)
@@ -109,9 +109,9 @@ void Protocol_Names(char *buffer, size_t buffersize)
        buffer[0] = 0;
        for (i = 0;protocolversioninfo[i].name;i++)
        {
-               if (i > 1)
-                       strlcat(buffer, " ", buffersize);
-               strlcat(buffer, protocolversioninfo[i].name, buffersize);
+               if (i > 0)
+                       dp_strlcat(buffer, " ", buffersize);
+               dp_strlcat(buffer, protocolversioninfo[i].name, buffersize);
        }
 }
 
index 047262d6a979604c1cefb5096f141674bdd8b2ab..92b98dba248374ee33ee479a07bae0f8a663e69b 100644 (file)
@@ -28,9 +28,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include "qstats.h"
 struct mempool_s;
 struct sizebuf_s;
-// protocolversion_t is defined in common.h
-enum protocolversion_e;
 
+// protocolversion_t is defined in common.h
 enum protocolversion_e Protocol_EnumForName(const char *s);
 const char *Protocol_NameForEnum(enum protocolversion_e p);
 enum protocolversion_e Protocol_EnumForNumber(int n);
@@ -109,80 +108,82 @@ void Protocol_Names(char *buffer, size_t buffersize);
 #define PFLAGS_FULLDYNAMIC             128 // must be set or the light fields are ignored
 
 // if the high bit of the servercmd is set, the low bits are fast update flags:
-#define U_MOREBITS             (1<<0)
-#define U_ORIGIN1              (1<<1)
-#define U_ORIGIN2              (1<<2)
-#define U_ORIGIN3              (1<<3)
-#define U_ANGLE2               (1<<4)
-// LadyHavoc: U_NOLERP was only ever used for monsters, so I renamed it U_STEP
-#define U_STEP                 (1<<5)
-#define U_FRAME                        (1<<6)
-// just differentiates from other updates
-#define U_SIGNAL               (1<<7)
-
-#define U_ANGLE1               (1<<8)
-#define U_ANGLE3               (1<<9)
-#define U_MODEL                        (1<<10)
-#define U_COLORMAP             (1<<11)
-#define U_SKIN                 (1<<12)
-#define U_EFFECTS              (1<<13)
-#define U_LONGENTITY   (1<<14)
+#define U_MOREBITS       (1u<<0)
+#define U_ORIGIN1        (1u<<1)
+#define U_ORIGIN2        (1u<<2)
+#define U_ORIGIN3        (1u<<3)
+#define U_ANGLE2         (1u<<4)
+// LadyHavoc: U_NOLERP was uonly ever used for monsters, so I renamed it U_STEP
+#define U_STEP           (1u<<5)
+#define U_FRAME          (1u<<6)
+// just differentiates fromu other updates
+#define U_SIGNAL         (1u<<7)
+
+#define U_ANGLE1         (1u<<8)
+#define U_ANGLE3         (1u<<9)
+#define U_MODEL          (1u<<10)
+#define U_COLORMAP       (1u<<11)
+#define U_SKIN           (1u<<12)
+#define U_EFFECTS        (1u<<13)
+#define U_LONGENTITY     (1u<<14)
 
 // LadyHavoc: protocol extension
-#define U_EXTEND1              (1<<15)
+#define U_EXTEND1        (1u<<15)
 // LadyHavoc: first extend byte
-#define U_DELTA                        (1<<16) // no data, while this is set the entity is delta compressed (uses previous frame as a baseline, meaning only things that have changed from the previous frame are sent, except for the forced full update every half second)
-#define U_ALPHA                        (1<<17) // 1 byte, 0.0-1.0 maps to 0-255, not sent if exactly 1, and the entity is not sent if <=0 unless it has effects (model effects are checked as well)
-#define U_SCALE                        (1<<18) // 1 byte, scale / 16 positive, not sent if 1.0
-#define U_EFFECTS2             (1<<19) // 1 byte, this is .effects & 0xFF00 (second byte)
-#define U_GLOWSIZE             (1<<20) // 1 byte, encoding is float/4.0, unsigned, not sent if 0
-#define U_GLOWCOLOR            (1<<21) // 1 byte, palette index, default is 254 (white), this IS used for darklight (allowing colored darklight), however the particles from a darklight are always black, not sent if default value (even if glowsize or glowtrail is set)
-#define U_COLORMOD             (1<<22) // 1 byte, 3 bit red, 3 bit green, 2 bit blue, this lets you tint an object artifically, so you could make a red rocket, or a blue fiend...
-#define U_EXTEND2              (1<<23) // another byte to follow
+#define U_DELTA          (1u<<16) ///< no data, while this is set the entity is delta compressed (uses previous frame as a baseline, meaning only things that have changed from the previous frame are sent, except for the forced full update every half second)
+#define U_ALPHA          (1u<<17) ///< 1 byte, 0.0-1.0 maps to 0-255, not sent if exactly 1, and the entity is not sent if <=0 unless it has effects (model effects are checked as well)
+#define U_SCALE          (1u<<18) ///< 1 byte, scale / 16 positive, not sent if 1.0
+#define U_EFFECTS2       (1u<<19) ///< 1 byte, this is .effects & 0xFF00 (second byte)
+#define U_GLOWSIZE       (1u<<20) ///< 1 byte, encoding is float/4.0, unsigned, not sent if 0
+#define U_GLOWCOLOR      (1u<<21) ///< 1 byte, palette index, default is 254 (white), this IS used for darklight (allowing colored darklight), however the particles from a darklight are always black, not sent if default value (even if glowsize or glowtrail is set)
+#define U_COLORMOD       (1u<<22) ///< 1 byte, 3 bit red, 3 bit green, 2 bit blue, this lets you tint an object artifically, so you could make a red rocket, or a blue fiend...
+#define U_EXTEND2        (1u<<23) ///< another byte to follow
 // LadyHavoc: second extend byte
-#define U_GLOWTRAIL            (1<<24) // leaves a trail of particles (of color .glowcolor, or black if it is a negative glowsize)
-#define U_VIEWMODEL            (1<<25) // attachs the model to the view (origin and angles become relative to it), only shown to owner, a more powerful alternative to .weaponmodel and such
-#define U_FRAME2               (1<<26) // 1 byte, this is .frame & 0xFF00 (second byte)
-#define U_MODEL2               (1<<27) // 1 byte, this is .modelindex & 0xFF00 (second byte)
-#define U_EXTERIORMODEL        (1<<28) // causes this model to not be drawn when using a first person view (third person will draw it, first person will not)
-#define U_UNUSED29             (1<<29) // future expansion
-#define U_UNUSED30             (1<<30) // future expansion
-#define U_EXTEND3              (1<<31) // another byte to follow, future expansion
-
-#define        SU_VIEWHEIGHT   (1<<0)
-#define        SU_IDEALPITCH   (1<<1)
-#define        SU_PUNCH1               (1<<2)
-#define        SU_PUNCH2               (1<<3)
-#define        SU_PUNCH3               (1<<4)
-#define        SU_VELOCITY1    (1<<5)
-#define        SU_VELOCITY2    (1<<6)
-#define        SU_VELOCITY3    (1<<7)
-//define       SU_AIMENT               (1<<8)  AVAILABLE BIT
-#define        SU_ITEMS                (1<<9)
-#define        SU_ONGROUND             (1<<10)         // no data follows, the bit is it
-#define        SU_INWATER              (1<<11)         // no data follows, the bit is it
-#define        SU_WEAPONFRAME  (1<<12)
-#define        SU_ARMOR                (1<<13)
-#define        SU_WEAPON               (1<<14)
-#define SU_EXTEND1             (1<<15)
+#define U_GLOWTRAIL      (1u<<24) ///< leaves a trail of particles (of color .glowcolor, or black if it is a negative glowsize)
+#define U_VIEWMODEL      (1u<<25) ///< attachs the model to the view (origin and angles become relative to it), only shown to owner, a more powerful alternative to .weaponmodel and such
+#define U_FRAME2         (1u<<26) ///< 1 byte, this is .frame & 0xFF00 (second byte)
+#define U_MODEL2         (1u<<27) ///< 1 byte, this is .modelindex & 0xFF00 (second byte)
+#define U_EXTERIORMODEL  (1u<<28) ///< causes this model to not be drawn when using a first person view (third person will draw it, first person will not)
+#define U_UNUSED29       (1u<<29) ///< future expansion
+#define U_UNUSED30       (1u<<30) ///< future expansion
+#define U_EXTEND3        (1u<<31) ///< another byte to follow, future expansion
+// UBSan: unsigned literals because left shifting by 31 causes signed overflow, although it works as expected on x86.
+
+#define SU_VIEWHEIGHT    (1u<<0)
+#define SU_IDEALPITCH    (1u<<1)
+#define SU_PUNCH1        (1u<<2)
+#define SU_PUNCH2        (1u<<3)
+#define SU_PUNCH3        (1u<<4)
+#define SU_VELOCITY1     (1u<<5)
+#define SU_VELOCITY2     (1u<<6)
+#define SU_VELOCITY3     (1u<<7)
+//#define SU_AIMENT        (1u<<8)  AVAILABLE BIT
+#define SU_ITEMS         (1u<<9)
+#define SU_ONGROUND      (1u<<10) ///< no data follows, the bit is it
+#define SU_INWATER       (1u<<11) ///< no data follows, the bit is it
+#define SU_WEAPONFRAME   (1u<<12)
+#define SU_ARMOR         (1u<<13)
+#define SU_WEAPON        (1u<<14)
+#define SU_EXTEND1       (1u<<15)
 // first extend byte
-#define SU_PUNCHVEC1   (1<<16)
-#define SU_PUNCHVEC2   (1<<17)
-#define SU_PUNCHVEC3   (1<<18)
-#define SU_VIEWZOOM            (1<<19) // byte factor (0 = 0.0 (not valid), 255 = 1.0)
-#define SU_UNUSED20            (1<<20)
-#define SU_UNUSED21            (1<<21)
-#define SU_UNUSED22            (1<<22)
-#define SU_EXTEND2             (1<<23) // another byte to follow, future expansion
+#define SU_PUNCHVEC1     (1u<<16)
+#define SU_PUNCHVEC2     (1u<<17)
+#define SU_PUNCHVEC3     (1u<<18)
+#define SU_VIEWZOOM      (1u<<19) ///< byte factor (0 = 0.0 (not valid), 255 = 1.0)
+#define SU_UNUSED20      (1u<<20)
+#define SU_UNUSED21      (1u<<21)
+#define SU_UNUSED22      (1u<<22)
+#define SU_EXTEND2       (1u<<23) ///< another byte to follow, future expansion
 // second extend byte
-#define SU_UNUSED24            (1<<24)
-#define SU_UNUSED25            (1<<25)
-#define SU_UNUSED26            (1<<26)
-#define SU_UNUSED27            (1<<27)
-#define SU_UNUSED28            (1<<28)
-#define SU_UNUSED29            (1<<29)
-#define SU_UNUSED30            (1<<30)
-#define SU_EXTEND3             (1<<31) // another byte to follow, future expansion
+#define SU_UNUSED24      (1u<<24)
+#define SU_UNUSED25      (1u<<25)
+#define SU_UNUSED26      (1u<<26)
+#define SU_UNUSED27      (1u<<27)
+#define SU_UNUSED28      (1u<<28)
+#define SU_UNUSED29      (1u<<29)
+#define SU_UNUSED30      (1u<<30)
+#define SU_EXTEND3       (1u<<31) ///< another byte to follow, future expansion
+// UBSan: unsigned literals because left shifting by 31 causes signed overflow, although it works as expected on x86.
 
 // a sound with no channel is a local only sound
 #define        SND_VOLUME              (1<<0)          // a byte
@@ -609,43 +610,44 @@ entityframe_database_t;
 // order of the bits to minimize overhead from extend bytes
 
 // enough to describe a nail, gib, shell casing, bullet hole, or rocket
-#define E_ORIGIN1              (1<<0)
-#define E_ORIGIN2              (1<<1)
-#define E_ORIGIN3              (1<<2)
-#define E_ANGLE1               (1<<3)
-#define E_ANGLE2               (1<<4)
-#define E_ANGLE3               (1<<5)
-#define E_MODEL1               (1<<6)
-#define E_EXTEND1              (1<<7)
+#define E_ORIGIN1        (1u<<0)
+#define E_ORIGIN2        (1u<<1)
+#define E_ORIGIN3        (1u<<2)
+#define E_ANGLE1         (1u<<3)
+#define E_ANGLE2         (1u<<4)
+#define E_ANGLE3         (1u<<5)
+#define E_MODEL1         (1u<<6)
+#define E_EXTEND1        (1u<<7)
 
 // enough to describe almost anything
-#define E_FRAME1               (1<<8)
-#define E_EFFECTS1             (1<<9)
-#define E_ALPHA                        (1<<10)
-#define E_SCALE                        (1<<11)
-#define E_COLORMAP             (1<<12)
-#define E_SKIN                 (1<<13)
-#define E_FLAGS                        (1<<14)
-#define E_EXTEND2              (1<<15)
+#define E_FRAME1         (1u<<8)
+#define E_EFFECTS1       (1u<<9)
+#define E_ALPHA          (1u<<10)
+#define E_SCALE          (1u<<11)
+#define E_COLORMAP       (1u<<12)
+#define E_SKIN           (1u<<13)
+#define E_FLAGS          (1u<<14)
+#define E_EXTEND2        (1u<<15)
 
 // players, custom color glows, high model numbers
-#define E_FRAME2               (1<<16)
-#define E_MODEL2               (1<<17)
-#define E_EFFECTS2             (1<<18)
-#define E_GLOWSIZE             (1<<19)
-#define E_GLOWCOLOR            (1<<20)
-#define E_LIGHT                        (1<<21)
-#define E_LIGHTPFLAGS  (1<<22)
-#define E_EXTEND3              (1<<23)
-
-#define E_SOUND1               (1<<24)
-#define E_SOUNDVOL             (1<<25)
-#define E_SOUNDATTEN   (1<<26)
-#define E_TAGATTACHMENT        (1<<27)
-#define E_LIGHTSTYLE   (1<<28)
-#define E_UNUSED6              (1<<29)
-#define E_UNUSED7              (1<<30)
-#define E_EXTEND4              (1<<31)
+#define E_FRAME2         (1u<<16)
+#define E_MODEL2         (1u<<17)
+#define E_EFFECTS2       (1u<<18)
+#define E_GLOWSIZE       (1u<<19)
+#define E_GLOWCOLOR      (1u<<20)
+#define E_LIGHT          (1u<<21)
+#define E_LIGHTPFLAGS    (1u<<22)
+#define E_EXTEND3        (1u<<23)
+
+#define E_SOUND1         (1u<<24)
+#define E_SOUNDVOL       (1u<<25)
+#define E_SOUNDATTEN     (1u<<26)
+#define E_TAGATTACHMENT  (1u<<27)
+#define E_LIGHTSTYLE     (1u<<28)
+#define E_UNUSED6        (1u<<29)
+#define E_UNUSED7        (1u<<30)
+#define E_EXTEND4        (1u<<31)
+// UBSan: unsigned literals because left shifting by 31 causes signed overflow, although it works as expected on x86.
 
 // returns difference between two states as E_ flags
 int EntityState_DeltaBits(const entity_state_t *o, const entity_state_t *n);
@@ -736,89 +738,90 @@ qbool EntityFrame4_WriteFrame(struct sizebuf_s *msg, int maxsize, entityframe4_d
 // reads a frame from the network stream
 void EntityFrame4_CL_ReadFrame(void);
 
-// reset all entity fields (typically used if status changed)
-#define E5_FULLUPDATE (1<<0)
-// E5_ORIGIN32=0: short[3] = s->origin[0] * 8, s->origin[1] * 8, s->origin[2] * 8
-// E5_ORIGIN32=1: float[3] = s->origin[0], s->origin[1], s->origin[2]
-#define E5_ORIGIN (1<<1)
-// E5_ANGLES16=0: byte[3] = s->angle[0] * 256 / 360, s->angle[1] * 256 / 360, s->angle[2] * 256 / 360
-// E5_ANGLES16=1: short[3] = s->angle[0] * 65536 / 360, s->angle[1] * 65536 / 360, s->angle[2] * 65536 / 360
-#define E5_ANGLES (1<<2)
-// E5_MODEL16=0: byte = s->modelindex
-// E5_MODEL16=1: short = s->modelindex
-#define E5_MODEL (1<<3)
-// E5_FRAME16=0: byte = s->frame
-// E5_FRAME16=1: short = s->frame
-#define E5_FRAME (1<<4)
-// byte = s->skin
-#define E5_SKIN (1<<5)
-// E5_EFFECTS16=0 && E5_EFFECTS32=0: byte = s->effects
-// E5_EFFECTS16=1 && E5_EFFECTS32=0: short = s->effects
-// E5_EFFECTS16=0 && E5_EFFECTS32=1: int = s->effects
-// E5_EFFECTS16=1 && E5_EFFECTS32=1: int = s->effects
-#define E5_EFFECTS (1<<6)
-// bits >= (1<<8)
-#define E5_EXTEND1 (1<<7)
-
-// byte = s->renderflags
-#define E5_FLAGS (1<<8)
-// byte = bound(0, s->alpha * 255, 255)
-#define E5_ALPHA (1<<9)
-// byte = bound(0, s->scale * 16, 255)
-#define E5_SCALE (1<<10)
-// flag
-#define E5_ORIGIN32 (1<<11)
-// flag
-#define E5_ANGLES16 (1<<12)
-// flag
-#define E5_MODEL16 (1<<13)
-// byte = s->colormap
-#define E5_COLORMAP (1<<14)
-// bits >= (1<<16)
-#define E5_EXTEND2 (1<<15)
-
-// short = s->tagentity
-// byte = s->tagindex
-#define E5_ATTACHMENT (1<<16)
-// short[4] = s->light[0], s->light[1], s->light[2], s->light[3]
-// byte = s->lightstyle
-// byte = s->lightpflags
-#define E5_LIGHT (1<<17)
-// byte = s->glowsize
-// byte = s->glowcolor
-#define E5_GLOW (1<<18)
-// short = s->effects
-#define E5_EFFECTS16 (1<<19)
-// int = s->effects
-#define E5_EFFECTS32 (1<<20)
-// flag
-#define E5_FRAME16 (1<<21)
-// byte[3] = s->colormod[0], s->colormod[1], s->colormod[2]
-#define E5_COLORMOD (1<<22)
-// bits >= (1<<24)
-#define E5_EXTEND3 (1<<23)
-
-// byte[3] = s->glowmod[0], s->glowmod[1], s->glowmod[2]
-#define E5_GLOWMOD (1<<24)
-// byte type=0 short frames[1] short times[1]
-// byte type=1 short frames[2] short times[2] byte lerps[2]
-// byte type=2 short frames[3] short times[3] byte lerps[3]
-// byte type=3 short frames[4] short times[4] byte lerps[4]
-// byte type=4 short modelindex byte numbones {short pose7s[7]}
-// see also RENDER_COMPLEXANIMATION
-#define E5_COMPLEXANIMATION (1<<25)
-// ushort traileffectnum
-#define E5_TRAILEFFECTNUM (1<<26)
-// unused
-#define E5_UNUSED27 (1<<27)
-// unused
-#define E5_UNUSED28 (1<<28)
-// unused
-#define E5_UNUSED29 (1<<29)
-// unused
-#define E5_UNUSED30 (1<<30)
-// bits2 > 0
-#define E5_EXTEND4 (1<<31)
+/// reset all entity fields (typically used if status changed)
+#define E5_FULLUPDATE        (1u<<0)
+/// E5_ORIGIN32=0: short[3] = s->origin[0] * 8, s->origin[1] * 8, s->origin[2] * 8
+/// E5_ORIGIN32=1: float[3] = s->origin[0], s->origin[1], s->origin[2]
+#define E5_ORIGIN            (1u<<1)
+/// E5_ANGLES16=0: byte[3] = s->angle[0] * 256 / 360, s->angle[1] * 256 / 360, s->angle[2] * 256 / 360
+/// E5_ANGLES16=1: short[3] = s->angle[0] * 65536 / 360, s->angle[1] * 65536 / 360, s->angle[2] * 65536 / 360
+#define E5_ANGLES            (1u<<2)
+/// E5_MODEL16=0: byte = s->modelindex
+/// E5_MODEL16=1: short = s->modelindex
+#define E5_MODEL             (1u<<3)
+/// E5_FRAME16=0: byte = s->frame
+/// E5_FRAME16=1: short = s->frame
+#define E5_FRAME             (1u<<4)
+/// byte = s->skin
+#define E5_SKIN              (1u<<5)
+/// E5_EFFECTS16=0 && E5_EFFECTS32=0: byte = s->effects
+/// E5_EFFECTS16=1 && E5_EFFECTS32=0: short = s->effects
+/// E5_EFFECTS16=0 && E5_EFFECTS32=1: int = s->effects
+/// E5_EFFECTS16=1 && E5_EFFECTS32=1: int = s->effects
+#define E5_EFFECTS           (1u<<6)
+/// bits >= (1<<8)
+#define E5_EXTEND1           (1u<<7)
+
+/// byte = s->renderflags
+#define E5_FLAGS             (1u<<8)
+/// byte = bound(0, s->alpha * 255, 255)
+#define E5_ALPHA             (1u<<9)
+/// byte = bound(0, s->scale * 16, 255)
+#define E5_SCALE             (1u<<10)
+/// flag
+#define E5_ORIGIN32          (1u<<11)
+/// flag
+#define E5_ANGLES16          (1u<<12)
+/// flag
+#define E5_MODEL16           (1u<<13)
+/// byte = s->colormap
+#define E5_COLORMAP          (1u<<14)
+/// bits >= (1<<16)
+#define E5_EXTEND2           (1u<<15)
+
+/// short = s->tagentity
+/// byte = s->tagindex
+#define E5_ATTACHMENT        (1u<<16)
+/// short[4] = s->light[0], s->light[1], s->light[2], s->light[3]
+/// byte = s->lightstyle
+/// byte = s->lightpflags
+#define E5_LIGHT             (1u<<17)
+/// byte = s->glowsize
+/// byte = s->glowcolor
+#define E5_GLOW              (1u<<18)
+/// short = s->effects
+#define E5_EFFECTS16         (1u<<19)
+/// int = s->effects
+#define E5_EFFECTS32         (1u<<20)
+/// flag
+#define E5_FRAME16           (1u<<21)
+/// byte[3] = s->colormod[0], s->colormod[1], s->colormod[2]
+#define E5_COLORMOD          (1u<<22)
+/// bits >= (1<<24)
+#define E5_EXTEND3           (1u<<23)
+
+/// byte[3] = s->glowmod[0], s->glowmod[1], s->glowmod[2]
+#define E5_GLOWMOD           (1u<<24)
+/// byte type=0 short frames[1] short times[1]
+/// byte type=1 short frames[2] short times[2] byte lerps[2]
+/// byte type=2 short frames[3] short times[3] byte lerps[3]
+/// byte type=3 short frames[4] short times[4] byte lerps[4]
+/// byte type=4 short modelindex byte numbones {short pose7s[7]}
+/// see also RENDER_COMPLEXANIMATION
+#define E5_COMPLEXANIMATION  (1u<<25)
+/// ushort traileffectnum
+#define E5_TRAILEFFECTNUM    (1u<<26)
+/// unused
+#define E5_UNUSED27          (1u<<27)
+/// unused
+#define E5_UNUSED28          (1u<<28)
+/// unused
+#define E5_UNUSED29          (1u<<29)
+/// unused
+#define E5_UNUSED30          (1u<<30)
+/// bits2 > 0
+#define E5_EXTEND4           (1u<<31)
+// UBSan: unsigned literals because left shifting by 31 causes signed overflow, although it works as expected on x86.
 
 #define ENTITYFRAME5_MAXPACKETLOGS 64
 #define ENTITYFRAME5_MAXSTATES 1024
index 3ef0b74cf939fc417757249a4e328f1a5d0dcfb8..1fb432c9824d4db3f4727924035e7c910df685ad 100644 (file)
@@ -27,12 +27,16 @@ void VM_Warning(prvm_prog_t *prog, const char *fmt, ...)
        va_list argptr;
        char msg[MAX_INPUTLINE];
        static double recursive = -1;
+       int outfd = sys.outfd;
+
+       // set output to stderr
+       sys.outfd = fileno(stderr);
 
        va_start(argptr,fmt);
        dpvsnprintf(msg,sizeof(msg),fmt,argptr);
        va_end(argptr);
 
-       Con_Printf(CON_WARN "%s", msg);
+       Con_Printf(CON_WARN "%s VM warning: %s", prog->name, msg);
 
        // TODO: either add a cvar/cmd to control the state dumping or replace some of the calls with Con_Printf [9/13/2006 Black]
        if(prvm_backtraceforwarnings.integer && recursive != host.realtime) // NOTE: this compares to the time, just in case if PRVM_PrintState causes a Host_Error and keeps recursive set
@@ -41,6 +45,9 @@ void VM_Warning(prvm_prog_t *prog, const char *fmt, ...)
                PRVM_PrintState(prog, 0);
                recursive = -1;
        }
+
+       // restore configured outfd
+       sys.outfd = outfd;
 }
 
 
@@ -58,13 +65,13 @@ void VM_CheckEmptyString(prvm_prog_t *prog, const char *s)
                prog->error_cmd("%s: Bad string", prog->name);
 }
 
-qbool PRVM_ConsoleCommand (prvm_prog_t *prog, const char *text, int *func, qbool preserve_self, int curself, double ptime, qbool prog_loaded, const char *error_message)
+qbool PRVM_ConsoleCommand(prvm_prog_t *prog, const char *text, size_t textlen, int *func, qbool preserve_self, int curself, double ptime, const char *error_message)
 {
        int restorevm_tempstringsbuf_cursize;
        int save_self = 0; // hush compiler warning
        qbool r = false;
 
-       if(!prog_loaded)
+       if(!prog->loaded)
                return false;
 
        if(func)
@@ -75,7 +82,7 @@ qbool PRVM_ConsoleCommand (prvm_prog_t *prog, const char *text, int *func, qbool
                        PRVM_gameglobalfloat(time) = ptime;
                PRVM_gameglobaledict(self) = curself;
                restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
-               PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(prog, text);
+               PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(prog, text, textlen);
                prog->ExecuteProgram(prog, *func, error_message);
                prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
                if(preserve_self)
@@ -264,20 +271,42 @@ void VM_RemoveEdictSkeleton(prvm_prog_t *prog, prvm_edict_t *ed)
 //============================================================================
 //BUILT-IN FUNCTIONS
 
-void VM_VarString(prvm_prog_t *prog, int first, char *out, int outlength)
+#ifdef WIN32
+       // memccpy() is standard in POSIX.1-2001, POSIX.1-2008, SVr4, 4.3BSD, C23.
+       // Microsoft supports it, but apparently complains if we use it.
+       #undef memccpy
+       #define memccpy _memccpy
+#endif
+size_t VM_VarString(prvm_prog_t *prog, int first, char *out, size_t outsize)
 {
        int i;
        const char *s;
-       char *outend;
+       char *p;
+       char *outend = out + outsize - 1;
 
-       outend = out + outlength - 1;
+       // bones_was_here: && out < outend improves perf significantly in some tests that don't trigger the warning,
+       // which seems odd, surely it would only help when the warning is printed?
        for (i = first;i < prog->argc && out < outend;i++)
        {
                s = PRVM_G_STRING((OFS_PARM0+i*3));
-               while (out < outend && *s)
-                       *out++ = *s++;
+               if (*s)
+               {
+                       // like dp_stpecpy but with a VM_Warning for use with `prvm_backtraceforwarnings 1`
+                       p = (char *)memccpy(out, s, '\0', (outend + 1) - out);
+                       if (p)
+                               out = p - 1;
+                       else
+                       {
+                               VM_Warning(prog, "%lu of %lu bytes available, will truncate %lu byte string \"%s\"\n", (unsigned long)(outend - out), (unsigned long)outsize - 1, (unsigned long)strlen(s), s);
+                               out = outend;
+                               *out = '\0';
+                       }
+               }
+               else
+                       *out = '\0';
        }
-       *out++ = 0;
+
+       return outsize - ((outend + 1) - out);
 }
 
 /*
@@ -321,6 +350,10 @@ static qbool checkextension(prvm_prog_t *prog, const char *name)
                        if (!strcasecmp("DP_QC_DIGEST_SHA256", name))
                                return Crypto_Available();
 
+                       // special shreck for libcurl
+                       if (!strcasecmp("DP_QC_URI_GET", name) || !strcasecmp("DP_QC_URI_POST", name))
+                               return Curl_Available();
+
                        return true;
                }
        }
@@ -347,7 +380,7 @@ error(value)
 void VM_error(prvm_prog_t *prog)
 {
        prvm_edict_t    *ed;
-       char string[VM_STRINGTEMP_LENGTH];
+       char string[VM_TEMPSTRING_MAXSIZE];
 
        VM_VarString(prog, 0, string, sizeof(string));
        Con_Printf(CON_ERROR "======%s ERROR in %s:\n%s\n", prog->name, PRVM_GetString(prog, prog->xfunction->s_name), string);
@@ -370,7 +403,7 @@ objerror(value)
 void VM_objerror(prvm_prog_t *prog)
 {
        prvm_edict_t    *ed;
-       char string[VM_STRINGTEMP_LENGTH];
+       char string[VM_TEMPSTRING_MAXSIZE];
 
        VM_VarString(prog, 0, string, sizeof(string));
        Con_Printf(CON_ERROR "======OBJECT ERROR======\n"); // , prog->name, PRVM_GetString(prog->xfunction->s_name), string); // or include them? FIXME
@@ -391,7 +424,7 @@ print(...[string])
 */
 void VM_print(prvm_prog_t *prog)
 {
-       char string[VM_STRINGTEMP_LENGTH];
+       char string[VM_TEMPSTRING_MAXSIZE];
 
        VM_VarString(prog, 0, string, sizeof(string));
        Con_Print(string);
@@ -408,7 +441,7 @@ bprint(...[string])
 */
 void VM_bprint(prvm_prog_t *prog)
 {
-       char string[VM_STRINGTEMP_LENGTH];
+       char string[VM_TEMPSTRING_MAXSIZE];
 
        if(!sv.active)
        {
@@ -433,7 +466,7 @@ void VM_sprint(prvm_prog_t *prog)
 {
        client_t        *client;
        int                     clientnum;
-       char string[VM_STRINGTEMP_LENGTH];
+       char string[VM_TEMPSTRING_MAXSIZE];
 
        VM_SAFEPARMCOUNTRANGE(1, 8, VM_sprint);
 
@@ -465,7 +498,7 @@ centerprint(value)
 */
 void VM_centerprint(prvm_prog_t *prog)
 {
-       char string[VM_STRINGTEMP_LENGTH];
+       char string[VM_TEMPSTRING_MAXSIZE];
 
        VM_SAFEPARMCOUNTRANGE(1, 8, VM_centerprint);
        VM_VarString(prog, 0, string, sizeof(string));
@@ -570,7 +603,7 @@ void VM_vectoangles(prvm_prog_t *prog)
 =================
 VM_random
 
-Returns a number from 0<= num < 1
+Returns a random number > 0 and < 1
 
 float random()
 =================
@@ -634,36 +667,18 @@ void VM_break(prvm_prog_t *prog)
 
 /*
 =================
-VM_localcmd_local
+VM_localcmd
 
-Sends text over to the client's execution buffer
+Appends text to the command buffer
 
 [localcmd (string, ...) or]
 cmd (string, ...)
 =================
 */
-void VM_localcmd_local(prvm_prog_t *prog)
+void VM_localcmd(prvm_prog_t *prog)
 {
-       char string[VM_STRINGTEMP_LENGTH];
-       VM_SAFEPARMCOUNTRANGE(1, 8, VM_localcmd_local);
-       VM_VarString(prog, 0, string, sizeof(string));
-       Cbuf_AddText(cmd_local, string);
-}
-
-/*
-=================
-VM_localcmd_server
-
-Sends text over to the server's execution buffer
-
-[localcmd (string, ...) or]
-cmd (string, ...)
-=================
-*/
-void VM_localcmd_server(prvm_prog_t *prog)
-{
-       char string[VM_STRINGTEMP_LENGTH];
-       VM_SAFEPARMCOUNTRANGE(1, 8, VM_localcmd_server);
+       char string[VM_TEMPSTRING_MAXSIZE];
+       VM_SAFEPARMCOUNTRANGE(1, 8, VM_localcmd);
        VM_VarString(prog, 0, string, sizeof(string));
        Cbuf_AddText(cmd_local, string);
 }
@@ -684,7 +699,7 @@ float cvar (string)
 */
 void VM_cvar(prvm_prog_t *prog)
 {
-       char string[VM_STRINGTEMP_LENGTH];
+       char string[VM_TEMPSTRING_MAXSIZE];
        VM_SAFEPARMCOUNTRANGE(1,8,VM_cvar);
        VM_VarString(prog, 0, string, sizeof(string));
        VM_CheckEmptyString(prog, string);
@@ -706,7 +721,7 @@ float CVAR_TYPEFLAG_READONLY = 32;
 */
 void VM_cvar_type(prvm_prog_t *prog)
 {
-       char string[VM_STRINGTEMP_LENGTH];
+       char string[VM_TEMPSTRING_MAXSIZE];
        cvar_t *cvar;
        int ret;
 
@@ -746,11 +761,18 @@ const string      VM_cvar_string (string, ...)
 */
 void VM_cvar_string(prvm_prog_t *prog)
 {
-       char string[VM_STRINGTEMP_LENGTH];
+       char cvar_name[VM_TEMPSTRING_MAXSIZE];
+
        VM_SAFEPARMCOUNTRANGE(1,8,VM_cvar_string);
-       VM_VarString(prog, 0, string, sizeof(string));
-       VM_CheckEmptyString(prog, string);
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, PRVM_Cvar_ReadOk(prog, string) ? Cvar_VariableString(prog->console_cmd->cvars, string, prog->console_cmd->cvars_flagsmask) : "");
+       VM_VarString(prog, 0, cvar_name, sizeof(cvar_name));
+       VM_CheckEmptyString(prog, cvar_name);
+       if (PRVM_Cvar_ReadOk(prog, cvar_name))
+       {
+               const char *cvar_string = Cvar_VariableString(prog->console_cmd->cvars, cvar_name, prog->console_cmd->cvars_flagsmask);
+               PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, cvar_string, strlen(cvar_string));
+       }
+       else
+               PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, "", 0);
 }
 
 
@@ -763,11 +785,14 @@ const string      VM_cvar_defstring (string, ...)
 */
 void VM_cvar_defstring(prvm_prog_t *prog)
 {
-       char string[VM_STRINGTEMP_LENGTH];
+       char cvar_name[VM_TEMPSTRING_MAXSIZE];
+       const char *cvar_defstring;
+
        VM_SAFEPARMCOUNTRANGE(1,8,VM_cvar_defstring);
-       VM_VarString(prog, 0, string, sizeof(string));
-       VM_CheckEmptyString(prog, string);
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, Cvar_VariableDefString(prog->console_cmd->cvars, string, prog->console_cmd->cvars_flagsmask));
+       VM_VarString(prog, 0, cvar_name, sizeof(cvar_name));
+       VM_CheckEmptyString(prog, cvar_name);
+       cvar_defstring = Cvar_VariableDefString(prog->console_cmd->cvars, cvar_name, prog->console_cmd->cvars_flagsmask);
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, cvar_defstring, strlen(cvar_defstring));
 }
 
 /*
@@ -779,11 +804,14 @@ const string      VM_cvar_description (string, ...)
 */
 void VM_cvar_description(prvm_prog_t *prog)
 {
-       char string[VM_STRINGTEMP_LENGTH];
+       char cvar_name[VM_TEMPSTRING_MAXSIZE];
+       const char *cvar_desc;
+
        VM_SAFEPARMCOUNTRANGE(1,8,VM_cvar_description);
-       VM_VarString(prog, 0, string, sizeof(string));
-       VM_CheckEmptyString(prog, string);
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, Cvar_VariableDescription(prog->console_cmd->cvars, string, prog->console_cmd->cvars_flagsmask));
+       VM_VarString(prog, 0, cvar_name, sizeof(cvar_name));
+       VM_CheckEmptyString(prog, cvar_name);
+       cvar_desc = Cvar_VariableDescription(prog->console_cmd->cvars, cvar_name, prog->console_cmd->cvars_flagsmask);
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, cvar_desc, strlen(cvar_desc));
 }
 /*
 =================
@@ -795,12 +823,25 @@ void cvar_set (string,string, ...)
 void VM_cvar_set(prvm_prog_t *prog)
 {
        const char *name;
-       char string[VM_STRINGTEMP_LENGTH];
+       char value[VM_TEMPSTRING_MAXSIZE];
+       cvar_t *cvar;
+
        VM_SAFEPARMCOUNTRANGE(2,8,VM_cvar_set);
-       VM_VarString(prog, 1, string, sizeof(string));
        name = PRVM_G_STRING(OFS_PARM0);
        VM_CheckEmptyString(prog, name);
-       Cvar_Set(prog->console_cmd->cvars, name, string);
+       cvar = Cvar_FindVar(prog->console_cmd->cvars, name, prog->console_cmd->cvars_flagsmask);
+       if (!cvar)
+       {
+               VM_Warning(prog, "VM_cvar_set: variable %s not found\n", name);
+               return;
+       }
+       if (cvar->flags & CF_READONLY)
+       {
+               VM_Warning(prog, "VM_cvar_set: variable %s is read-only\n", cvar->name);
+               return;
+       }
+       VM_VarString(prog, 1, value, sizeof(value));
+       Cvar_SetQuick(cvar, value);
 }
 
 /*
@@ -812,7 +853,7 @@ dprint(...[string])
 */
 void VM_dprint(prvm_prog_t *prog)
 {
-       char string[VM_STRINGTEMP_LENGTH];
+       char string[VM_TEMPSTRING_MAXSIZE];
        VM_SAFEPARMCOUNTRANGE(1, 8, VM_dprint);
        VM_VarString(prog, 0, string, sizeof(string));
 #if 1
@@ -834,16 +875,17 @@ void VM_ftos(prvm_prog_t *prog)
 {
        prvm_vec_t v;
        char s[128];
+       size_t slen;
 
        VM_SAFEPARMCOUNT(1, VM_ftos);
 
        v = PRVM_G_FLOAT(OFS_PARM0);
 
        if ((prvm_vec_t)((prvm_int_t)v) == v)
-               dpsnprintf(s, sizeof(s), "%.0f", v);
+               slen = dpsnprintf(s, sizeof(s), "%.0f", v);
        else
-               dpsnprintf(s, sizeof(s), "%f", v);
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, s);
+               slen = dpsnprintf(s, sizeof(s), "%f", v);
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, s, slen);
 }
 
 /*
@@ -875,11 +917,12 @@ string    vtos(vector)
 void VM_vtos(prvm_prog_t *prog)
 {
        char s[512];
+       size_t slen;
 
        VM_SAFEPARMCOUNT(1,VM_vtos);
 
-       dpsnprintf (s, sizeof(s), "'%5.1f %5.1f %5.1f'", PRVM_G_VECTOR(OFS_PARM0)[0], PRVM_G_VECTOR(OFS_PARM0)[1], PRVM_G_VECTOR(OFS_PARM0)[2]);
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, s);
+       slen = dpsnprintf(s, sizeof(s), "'%5.1f %5.1f %5.1f'", PRVM_G_VECTOR(OFS_PARM0)[0], PRVM_G_VECTOR(OFS_PARM0)[1], PRVM_G_VECTOR(OFS_PARM0)[2]);
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, s, slen);
 }
 
 /*
@@ -893,11 +936,12 @@ string    etos(entity)
 void VM_etos(prvm_prog_t *prog)
 {
        char s[128];
+       size_t slen;
 
        VM_SAFEPARMCOUNT(1, VM_etos);
 
-       dpsnprintf (s, sizeof(s), "entity %i", PRVM_G_EDICTNUM(OFS_PARM0));
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, s);
+       slen = dpsnprintf(s, sizeof(s), "entity %i", PRVM_G_EDICTNUM(OFS_PARM0));
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, s, slen);
 }
 
 /*
@@ -909,7 +953,7 @@ float stof(...[string])
 */
 void VM_stof(prvm_prog_t *prog)
 {
-       char string[VM_STRINGTEMP_LENGTH];
+       char string[VM_TEMPSTRING_MAXSIZE];
        VM_SAFEPARMCOUNTRANGE(1, 8, VM_stof);
        VM_VarString(prog, 0, string, sizeof(string));
        PRVM_G_FLOAT(OFS_RETURN) = atof(string);
@@ -976,8 +1020,10 @@ void VM_strftime(prvm_prog_t *prog)
 #else
        struct tm *tm;
 #endif
-       char fmt[VM_STRINGTEMP_LENGTH];
-       char result[VM_STRINGTEMP_LENGTH];
+       char fmt[VM_TEMPSTRING_MAXSIZE];
+       char result[VM_TEMPSTRING_MAXSIZE];
+       size_t result_len;
+
        VM_SAFEPARMCOUNTRANGE(2, 8, VM_strftime);
        VM_VarString(prog, 1, fmt, sizeof(fmt));
        t = time(NULL);
@@ -999,11 +1045,11 @@ void VM_strftime(prvm_prog_t *prog)
                return;
        }
 #if _MSC_VER >= 1400
-       strftime(result, sizeof(result), fmt, &tm);
+       result_len = strftime(result, sizeof(result), fmt, &tm);
 #else
-       strftime(result, sizeof(result), fmt, tm);
+       result_len = strftime(result, sizeof(result), fmt, tm);
 #endif
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, result);
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, result, result_len);
 }
 
 /*
@@ -1214,7 +1260,7 @@ void VM_findchainfloat(prvm_prog_t *prog)
        else
                chainfield = prog->fieldoffsets.chain;
        if (chainfield < 0)
-               prog->error_cmd("VM_findchain: %s doesnt have the specified chain field !", prog->name);
+               prog->error_cmd("VM_findchainfloat: %s doesnt have the specified chain field !", prog->name);
 
        chain = (prvm_edict_t *)prog->edicts;
 
@@ -1300,7 +1346,7 @@ void VM_findchainflags(prvm_prog_t *prog)
        else
                chainfield = prog->fieldoffsets.chain;
        if (chainfield < 0)
-               prog->error_cmd("VM_findchain: %s doesnt have the specified chain field !", prog->name);
+               prog->error_cmd("VM_findchainflags: %s doesnt have the specified chain field !", prog->name);
 
        chain = (prvm_edict_t *)prog->edicts;
 
@@ -1690,7 +1736,7 @@ float     registercvar (string name, string value[, float flags])
 void VM_registercvar(prvm_prog_t *prog)
 {
        const char *name, *value;
-       int     flags;
+       unsigned flags;
 
        VM_SAFEPARMCOUNTRANGE(2, 3, VM_registercvar);
 
@@ -1950,7 +1996,7 @@ string    fgets(float fhandle)
 void VM_fgets(prvm_prog_t *prog)
 {
        int c, end;
-       char string[VM_STRINGTEMP_LENGTH];
+       char string[VM_TEMPSTRING_MAXSIZE];
        int filenum;
 
        VM_SAFEPARMCOUNT(1,VM_fgets);
@@ -1975,10 +2021,10 @@ void VM_fgets(prvm_prog_t *prog)
                c = FS_Getc(prog->openfiles[filenum]);
                if (c == '\r' || c == '\n' || c < 0)
                        break;
-               if (end < VM_STRINGTEMP_LENGTH - 1)
+               if (end < VM_TEMPSTRING_MAXSIZE - 1)
                        string[end++] = c;
        }
-       string[end] = 0;
+       string[end] = '\0';
        // remove \n following \r
        if (c == '\r')
        {
@@ -1989,7 +2035,7 @@ void VM_fgets(prvm_prog_t *prog)
        if (developer_extra.integer)
                Con_DPrintf("fgets: %s: %s\n", prog->name, string);
        if (c >= 0 || end)
-               PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, string);
+               PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, string, end);
 }
 
 /*
@@ -2002,8 +2048,8 @@ fputs(float fhandle, string s)
 //void(float fhandle, string s) fputs = #113; // writes a line of text to the end of the file
 void VM_fputs(prvm_prog_t *prog)
 {
-       int stringlength;
-       char string[VM_STRINGTEMP_LENGTH];
+       size_t stringlength;
+       char string[VM_TEMPSTRING_MAXSIZE];
        int filenum;
 
        VM_SAFEPARMCOUNT(2,VM_fputs);
@@ -2019,8 +2065,8 @@ void VM_fputs(prvm_prog_t *prog)
                VM_Warning(prog, "VM_fputs: no such file handle %i (or file has been closed) in %s\n", filenum, prog->name);
                return;
        }
-       VM_VarString(prog, 1, string, sizeof(string));
-       if ((stringlength = (int)strlen(string)))
+       stringlength = VM_VarString(prog, 1, string, sizeof(string));
+       if (stringlength)
                FS_Write(prog->openfiles[filenum], string, stringlength);
        if (developer_extra.integer)
                Con_DPrintf("fputs: %s: %s\n", prog->name, string);
@@ -2088,7 +2134,7 @@ void VM_entityfieldname(prvm_prog_t *prog)
        if (i < 0 || i >= prog->numfielddefs)
        {
                VM_Warning(prog, "VM_entityfieldname: %s: field index out of bounds\n", prog->name);
-               PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, "");
+               PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, "", 0);
                return;
        }
 
@@ -2140,8 +2186,8 @@ void VM_getentityfieldstring(prvm_prog_t *prog)
        
        if (i < 0 || i >= prog->numfielddefs)
        {
-        VM_Warning(prog, "VM_entityfielddata: %s: field index out of bounds\n", prog->name);
-               PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, "");
+               VM_Warning(prog, "VM_entityfielddata: %s: field index out of bounds\n", prog->name);
+               PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, "", 0);
                return;
        }
        
@@ -2151,7 +2197,7 @@ void VM_getentityfieldstring(prvm_prog_t *prog)
        ent = PRVM_G_EDICT(OFS_PARM1);
        if(ent->free)
        {
-               PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, "");
+               PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, "", 0);
                VM_Warning(prog, "VM_entityfielddata: %s: entity %i is free !\n", prog->name, PRVM_NUM_FOR_EDICT(ent));
                return;
        }
@@ -2164,11 +2210,12 @@ void VM_getentityfieldstring(prvm_prog_t *prog)
                        break;
        if (j == prvm_type_size[type])
        {
-               PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, "");
+               PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, "", 0);
                return;
        }
-               
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, PRVM_UglyValueString(prog, (etype_t)d->type, val, valuebuf, sizeof(valuebuf)));
+
+       PRVM_UglyValueString(prog, (etype_t)d->type, val, valuebuf, sizeof(valuebuf));
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, valuebuf, strlen(valuebuf));
 }
 
 // KrimZon - DP_QC_ENTITYDATA
@@ -2234,14 +2281,15 @@ string  strdecolorize(string s)
 // string (string s) strdecolorize = #472; // returns the passed in string with color codes stripped
 void VM_strdecolorize(prvm_prog_t *prog)
 {
-       char szNewString[VM_STRINGTEMP_LENGTH];
+       char szNewString[VM_TEMPSTRING_MAXSIZE];
+       size_t szNewString_len;
        const char *szString;
 
        // Prepare Strings
        VM_SAFEPARMCOUNT(1,VM_strdecolorize);
        szString = PRVM_G_STRING(OFS_PARM0);
-       COM_StringDecolorize(szString, 0, szNewString, sizeof(szNewString), true);
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, szNewString);
+       szNewString_len = COM_StringDecolorize(szString, 0, szNewString, sizeof(szNewString), true);
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, szNewString, szNewString_len);
 }
 
 // DRESK - String Length (not counting color codes)
@@ -2280,16 +2328,17 @@ string  strtolower(string s)
 // string (string s) strtolower = #480; // returns passed in string in lowercase form
 void VM_strtolower(prvm_prog_t *prog)
 {
-       char szNewString[VM_STRINGTEMP_LENGTH];
+       char szNewString[VM_TEMPSTRING_MAXSIZE];
+       size_t szNewString_len;
        const char *szString;
 
        // Prepare Strings
        VM_SAFEPARMCOUNT(1,VM_strtolower);
        szString = PRVM_G_STRING(OFS_PARM0);
 
-       COM_ToLowerString(szString, szNewString, sizeof(szNewString) );
+       szNewString_len = COM_ToLowerString(szString, szNewString, sizeof(szNewString) );
 
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, szNewString);
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, szNewString, szNewString_len);
 }
 
 /*
@@ -2302,35 +2351,38 @@ string  strtoupper(string s)
 // string (string s) strtoupper = #481; // returns passed in string in uppercase form
 void VM_strtoupper(prvm_prog_t *prog)
 {
-       char szNewString[VM_STRINGTEMP_LENGTH];
+       char szNewString[VM_TEMPSTRING_MAXSIZE];
+       size_t szNewString_len;
        const char *szString;
 
        // Prepare Strings
        VM_SAFEPARMCOUNT(1,VM_strtoupper);
        szString = PRVM_G_STRING(OFS_PARM0);
 
-       COM_ToUpperString(szString, szNewString, sizeof(szNewString) );
+       szNewString_len = COM_ToUpperString(szString, szNewString, sizeof(szNewString) );
 
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, szNewString);
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, szNewString, szNewString_len);
 }
 
 /*
 =========
 VM_strcat
 
-string strcat(string,string,...[string])
+string strcat(string s, string...)
 =========
 */
-//string(string s1, string s2) strcat = #115;
-// concatenates two strings (for example "abc", "def" would return "abcdef")
+//string(string s, string...) strcat = #115;
+// concatenates strings (for example "abc", "def" would return "abcdef")
 // and returns as a tempstring
 void VM_strcat(prvm_prog_t *prog)
 {
-       char s[VM_STRINGTEMP_LENGTH];
+       char s[VM_TEMPSTRING_MAXSIZE];
+       size_t slen;
+
        VM_SAFEPARMCOUNTRANGE(1, 8, VM_strcat);
 
-       VM_VarString(prog, 0, s, sizeof(s));
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, s);
+       slen = VM_VarString(prog, 0, s, sizeof(s));
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, s, slen);
 }
 
 /*
@@ -2348,7 +2400,7 @@ void VM_substring(prvm_prog_t *prog)
        int u_slength = 0, u_start;
        size_t u_length;
        const char *s;
-       char string[VM_STRINGTEMP_LENGTH];
+       char string[VM_TEMPSTRING_MAXSIZE];
 
        VM_SAFEPARMCOUNT(3,VM_substring);
 
@@ -2394,7 +2446,7 @@ void VM_substring(prvm_prog_t *prog)
        u_start = u8_byteofs(s, start, NULL);
        if (u_start < 0)
        {
-               PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, "");
+               PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, "", 0);
                return;
        }
        u_length = u8_bytelen(s + u_start, length);
@@ -2402,8 +2454,8 @@ void VM_substring(prvm_prog_t *prog)
                u_length = sizeof(string)-1;
        
        memcpy(string, s + u_start, u_length);
-       string[u_length] = 0;
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, string);
+       string[u_length] = '\0';
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, string, u_length);
 }
 
 /*
@@ -2418,7 +2470,7 @@ void VM_strreplace(prvm_prog_t *prog)
 {
        int i, j, si;
        const char *search, *replace, *subject;
-       char string[VM_STRINGTEMP_LENGTH];
+       char string[VM_TEMPSTRING_MAXSIZE];
        int search_len, replace_len, subject_len;
 
        VM_SAFEPARMCOUNT(3,VM_strreplace);
@@ -2470,7 +2522,7 @@ void VM_strreplace(prvm_prog_t *prog)
                        string[si++] = subject[i];
        string[si] = '\0';
 
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, string);
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, string, si);
 }
 
 /*
@@ -2485,7 +2537,7 @@ void VM_strireplace(prvm_prog_t *prog)
 {
        int i, j, si;
        const char *search, *replace, *subject;
-       char string[VM_STRINGTEMP_LENGTH];
+       char string[VM_TEMPSTRING_MAXSIZE];
        int search_len, replace_len, subject_len;
 
        VM_SAFEPARMCOUNT(3, VM_strireplace);
@@ -2537,7 +2589,7 @@ void VM_strireplace(prvm_prog_t *prog)
                        string[si++] = subject[i];
        string[si] = '\0';
 
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, string);
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, string, si);
 }
 
 /*
@@ -2550,7 +2602,7 @@ vector    stov(string s)
 //vector(string s) stov = #117; // returns vector value from a string
 void VM_stov(prvm_prog_t *prog)
 {
-       char string[VM_STRINGTEMP_LENGTH];
+       char string[VM_TEMPSTRING_MAXSIZE];
 
        VM_SAFEPARMCOUNT(1,VM_stov);
 
@@ -2569,13 +2621,12 @@ string  strzone(string s)
 void VM_strzone(prvm_prog_t *prog)
 {
        char *out;
-       char string[VM_STRINGTEMP_LENGTH];
+       char string[VM_TEMPSTRING_MAXSIZE];
        size_t alloclen;
 
        VM_SAFEPARMCOUNT(1,VM_strzone);
 
-       VM_VarString(prog, 0, string, sizeof(string));
-       alloclen = strlen(string) + 1;
+       alloclen = VM_VarString(prog, 0, string, sizeof(string)) + 1;
        PRVM_G_INT(OFS_RETURN) = PRVM_AllocString(prog, alloclen, &out);
        memcpy(out, string, alloclen);
 }
@@ -2605,17 +2656,17 @@ float tokenize(string s)
 //this function originally written by KrimZon, made shorter by LadyHavoc
 //20040203: rewritten by LadyHavoc (no longer uses allocations)
 static int num_tokens = 0;
-static int tokens[VM_STRINGTEMP_LENGTH / 2];
-static int tokens_startpos[VM_STRINGTEMP_LENGTH / 2];
-static int tokens_endpos[VM_STRINGTEMP_LENGTH / 2];
-static char tokenize_string[VM_STRINGTEMP_LENGTH];
+static int tokens[VM_TEMPSTRING_MAXSIZE / 2];
+static int tokens_startpos[VM_TEMPSTRING_MAXSIZE / 2];
+static int tokens_endpos[VM_TEMPSTRING_MAXSIZE / 2];
+static char tokenize_string[VM_TEMPSTRING_MAXSIZE];
 void VM_tokenize (prvm_prog_t *prog)
 {
        const char *p;
 
        VM_SAFEPARMCOUNT(1,VM_tokenize);
 
-       strlcpy(tokenize_string, PRVM_G_STRING(OFS_PARM0), sizeof(tokenize_string));
+       dp_strlcpy(tokenize_string, PRVM_G_STRING(OFS_PARM0), sizeof(tokenize_string));
        p = tokenize_string;
 
        num_tokens = 0;
@@ -2632,7 +2683,7 @@ void VM_tokenize (prvm_prog_t *prog)
                if(!COM_ParseToken_VM_Tokenize(&p, false))
                        break;
                tokens_endpos[num_tokens] = p - tokenize_string;
-               tokens[num_tokens] = PRVM_SetTempString(prog, com_token);
+               tokens[num_tokens] = PRVM_SetTempString(prog, com_token, com_token_len);
                ++num_tokens;
        }
 
@@ -2646,7 +2697,7 @@ void VM_tokenize_console (prvm_prog_t *prog)
 
        VM_SAFEPARMCOUNT(1, VM_tokenize_console);
 
-       strlcpy(tokenize_string, PRVM_G_STRING(OFS_PARM0), sizeof(tokenize_string));
+       dp_strlcpy(tokenize_string, PRVM_G_STRING(OFS_PARM0), sizeof(tokenize_string));
        p = tokenize_string;
 
        num_tokens = 0;
@@ -2663,7 +2714,7 @@ void VM_tokenize_console (prvm_prog_t *prog)
                if(!COM_ParseToken_Console(&p))
                        break;
                tokens_endpos[num_tokens] = p - tokenize_string;
-               tokens[num_tokens] = PRVM_SetTempString(prog, com_token);
+               tokens[num_tokens] = PRVM_SetTempString(prog, com_token, com_token_len);
                ++num_tokens;
        }
 
@@ -2696,7 +2747,7 @@ void VM_tokenizebyseparator (prvm_prog_t *prog)
 
        VM_SAFEPARMCOUNTRANGE(2, 8,VM_tokenizebyseparator);
 
-       strlcpy(tokenize_string, PRVM_G_STRING(OFS_PARM0), sizeof(tokenize_string));
+       dp_strlcpy(tokenize_string, PRVM_G_STRING(OFS_PARM0), sizeof(tokenize_string));
        p = tokenize_string;
 
        numseparators = 0;
@@ -2739,8 +2790,8 @@ void VM_tokenizebyseparator (prvm_prog_t *prog)
                tokens_endpos[num_tokens] = p0 - tokenize_string;
                if (j >= (int)sizeof(tokentext))
                        break;
-               tokentext[j++] = 0;
-               tokens[num_tokens++] = PRVM_SetTempString(prog, token);
+               tokentext[j++] = '\0';
+               tokens[num_tokens++] = PRVM_SetTempString(prog, token, j - 1);
                if (!*p)
                        break;
        }
@@ -3064,13 +3115,21 @@ float   mod(float val, float m)
 */
 void VM_modulo(prvm_prog_t *prog)
 {
-       prvm_int_t val, m;
+       vec_t val, m;
+
        VM_SAFEPARMCOUNT(2, VM_modulo);
 
-       val = (prvm_int_t) PRVM_G_FLOAT(OFS_PARM0);
-       m       = (prvm_int_t) PRVM_G_FLOAT(OFS_PARM1);
+       val = PRVM_G_FLOAT(OFS_PARM0);
+       m   = PRVM_G_FLOAT(OFS_PARM1);
 
-       PRVM_G_FLOAT(OFS_RETURN) = (prvm_vec_t) (val % m);
+       // matches how gmqcc implements % when mod() builtin isn't defined, and FTEQW mod()
+       if (m)
+               PRVM_G_FLOAT(OFS_RETURN) = val - m * (prvm_int_t)(val / m);
+       else
+       {
+               VM_Warning(prog, "Attempted modulo of %f by zero\n", val);
+               PRVM_G_FLOAT(OFS_RETURN) = 0;
+       }
 }
 
 static void VM_Search_Init(prvm_prog_t *prog)
@@ -3207,6 +3266,7 @@ string    search_getfilename(float handle, float num)
 void VM_search_getfilename(prvm_prog_t *prog)
 {
        int handle, filenum;
+
        VM_SAFEPARMCOUNT(2, VM_search_getfilename);
 
        handle = (int)PRVM_G_FLOAT(OFS_PARM0);
@@ -3228,7 +3288,9 @@ void VM_search_getfilename(prvm_prog_t *prog)
                return;
        }
 
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, prog->opensearches[handle]->filenames[filenum]);
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog,
+                                                   prog->opensearches[handle]->filenames[filenum],
+                                                   strlen(prog->opensearches[handle]->filenames[filenum]));
 }
 
 /*
@@ -3252,11 +3314,11 @@ void VM_chr(prvm_prog_t *prog)
        
        char tmp[8];
        int len;
-       VM_SAFEPARMCOUNT(1, VM_chr);
 
+       VM_SAFEPARMCOUNT(1, VM_chr);
        len = u8_fromchar((Uchar)PRVM_G_FLOAT(OFS_PARM0), tmp, sizeof(tmp));
-       tmp[len] = 0;
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, tmp);
+       tmp[len] = '\0';
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, tmp, len);
 }
 
 /*
@@ -3268,10 +3330,12 @@ string keynumtostring(float keynum)
 */
 void VM_keynumtostring (prvm_prog_t *prog)
 {
-       char tinystr[2];
-       VM_SAFEPARMCOUNT(1, VM_keynumtostring);
+       char tinystr[TINYSTR_LEN];
+       const char *str; // Key_KeynumToString doesn't always return tinystr
 
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, Key_KeynumToString((int)PRVM_G_FLOAT(OFS_PARM0), tinystr, sizeof(tinystr)));
+       VM_SAFEPARMCOUNT(1, VM_keynumtostring);
+       str = Key_KeynumToString((int)PRVM_G_FLOAT(OFS_PARM0), tinystr, sizeof(tinystr));
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, str, strlen(str));
 }
 
 /*
@@ -3288,7 +3352,8 @@ void M_FindKeysForCommand(const char *command, int *keys);
 void VM_findkeysforcommand(prvm_prog_t *prog)
 {
        const char *cmd;
-       char ret[VM_STRINGTEMP_LENGTH];
+       char ret[VM_TEMPSTRING_MAXSIZE];
+       size_t ret_len;
        int keys[FKFC_NUMKEYS];
        int i;
        int bindmap;
@@ -3308,9 +3373,9 @@ void VM_findkeysforcommand(prvm_prog_t *prog)
 
        ret[0] = 0;
        for(i = 0; i < FKFC_NUMKEYS; i++)
-               strlcat(ret, va(vabuf, sizeof(vabuf), " \'%i\'", keys[i]), sizeof(ret));
+               ret_len = dp_strlcat(ret, va(vabuf, sizeof(vabuf), " \'%i\'", keys[i]), sizeof(ret));
 
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, ret);
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, ret, ret_len);
 }
 
 /*
@@ -3337,13 +3402,15 @@ string getkeybind(float key, float bindmap)
 void VM_getkeybind (prvm_prog_t *prog)
 {
        int bindmap;
+       const char *bind;
+
        VM_SAFEPARMCOUNTRANGE(1, 2, VM_getkeybind);
        if(prog->argc == 2)
                bindmap = bound(-1, PRVM_G_FLOAT(OFS_PARM1), MAX_BINDMAPS-1);
        else
                bindmap = 0; // consistent to "bind"
-
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, Key_GetBind((int)PRVM_G_FLOAT(OFS_PARM0), bindmap));
+       bind = Key_GetBind((int)PRVM_G_FLOAT(OFS_PARM0), bindmap);
+       PRVM_G_INT(OFS_RETURN) = bind ? PRVM_SetTempString(prog, bind, strlen(bind)) : 0;
 }
 
 /*
@@ -3584,7 +3651,7 @@ string altstr_prepare(string)
 void VM_altstr_prepare(prvm_prog_t *prog)
 {
        const char *instr, *in;
-       char outstr[VM_STRINGTEMP_LENGTH];
+       char outstr[VM_TEMPSTRING_MAXSIZE];
        size_t outpos;
 
        VM_SAFEPARMCOUNT( 1, VM_altstr_prepare );
@@ -3601,9 +3668,9 @@ void VM_altstr_prepare(prvm_prog_t *prog)
                else
                        outstr[outpos++] = *in;
        }
-       outstr[outpos] = 0;
 
-       PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString(prog,  outstr );
+       outstr[outpos] = '\0';
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, outstr, outpos);
 }
 
 /*
@@ -3618,7 +3685,7 @@ void VM_altstr_get(prvm_prog_t *prog)
        const char *altstr, *pos;
        char *out;
        int count, size;
-       char outstr[VM_STRINGTEMP_LENGTH];
+       char outstr[VM_TEMPSTRING_MAXSIZE];
 
        VM_SAFEPARMCOUNT( 2, VM_altstr_get );
 
@@ -3650,8 +3717,8 @@ void VM_altstr_get(prvm_prog_t *prog)
                else
                        *out = *pos;
 
-       *out = 0;
-       PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString(prog,  outstr );
+       *out = '\0';
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, outstr, out - outstr);
 }
 
 /*
@@ -3667,7 +3734,7 @@ void VM_altstr_set(prvm_prog_t *prog)
        const char *altstr, *str;
        const char *in;
        char *out;
-       char outstr[VM_STRINGTEMP_LENGTH];
+       char outstr[VM_TEMPSTRING_MAXSIZE];
 
        VM_SAFEPARMCOUNT( 3, VM_altstr_set );
 
@@ -3694,8 +3761,8 @@ void VM_altstr_set(prvm_prog_t *prog)
                if( *in == '\'' || (*in == '\\' && !*++in) )
                        break;
 
-       strlcpy(out, in, outstr + sizeof(outstr) - out);
-       PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString(prog,  outstr );
+       out += dp_strlcpy(out, in, outstr + sizeof(outstr) - out);
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, outstr, out - outstr);
 }
 
 /*
@@ -3711,7 +3778,7 @@ void VM_altstr_ins(prvm_prog_t *prog)
        const char *set;
        const char *in;
        char *out;
-       char outstr[VM_STRINGTEMP_LENGTH];
+       char outstr[VM_TEMPSTRING_MAXSIZE];
 
        VM_SAFEPARMCOUNT(3, VM_altstr_ins);
 
@@ -3733,8 +3800,8 @@ void VM_altstr_ins(prvm_prog_t *prog)
        for( ; *set ; *out++ = *set++ );
        *out++ = '\'';
 
-       strlcpy(out, in, outstr + sizeof(outstr) - out);
-       PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString(prog,  outstr );
+       out += dp_strlcpy(out, in, outstr + sizeof(outstr) - out);
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, outstr, out - outstr);
 }
 
 
@@ -3797,7 +3864,7 @@ static int BufStr_SortStringsDOWN (const void *in1, const void *in2)
        return strncmp(b, a, stringbuffers_sortlength);
 }
 
-prvm_stringbuffer_t *BufStr_FindCreateReplace (prvm_prog_t *prog, int bufindex, int flags, const char *format)
+prvm_stringbuffer_t *BufStr_FindCreateReplace (prvm_prog_t *prog, int bufindex, unsigned flags, const char *format)
 {
        prvm_stringbuffer_t *stringbuffer;
        int i;
@@ -4061,10 +4128,12 @@ string buf_implode(float bufhandle, string glue) = #465;
 void VM_buf_implode (prvm_prog_t *prog)
 {
        prvm_stringbuffer_t *stringbuffer;
-       char                    k[VM_STRINGTEMP_LENGTH];
-       const char              *sep;
-       int                             i;
-       size_t                  l;
+       char k[VM_TEMPSTRING_MAXSIZE];
+       size_t k_len;
+       const char *sep;
+       int i;
+       size_t l;
+
        VM_SAFEPARMCOUNT(2, VM_buf_implode);
 
        stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
@@ -4077,7 +4146,8 @@ void VM_buf_implode (prvm_prog_t *prog)
        if(!stringbuffer->num_strings)
                return;
        sep = PRVM_G_STRING(OFS_PARM1);
-       k[0] = 0;
+       k[0] = '\0';
+       k_len = 0;
        for(l = i = 0;i < stringbuffer->num_strings;i++)
        {
                if(stringbuffer->strings[i])
@@ -4085,11 +4155,11 @@ void VM_buf_implode (prvm_prog_t *prog)
                        l += (i > 0 ? strlen(sep) : 0) + strlen(stringbuffer->strings[i]);
                        if (l >= sizeof(k) - 1)
                                break;
-                       strlcat(k, sep, sizeof(k));
-                       strlcat(k, stringbuffer->strings[i], sizeof(k));
+                       dp_strlcat(k, sep, sizeof(k));
+                       k_len = dp_strlcat(k, stringbuffer->strings[i], sizeof(k));
                }
        }
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, k);
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, k, k_len);
 }
 
 /*
@@ -4119,7 +4189,7 @@ void VM_bufstr_get (prvm_prog_t *prog)
                return;
        }
        if (strindex < stringbuffer->num_strings && stringbuffer->strings[strindex])
-               PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, stringbuffer->strings[strindex]);
+               PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, stringbuffer->strings[strindex], strlen(stringbuffer->strings[strindex]));
 }
 
 /*
@@ -4249,7 +4319,7 @@ void VM_buf_loadfile(prvm_prog_t *prog)
 {
        size_t alloclen;
        prvm_stringbuffer_t *stringbuffer;
-       char string[VM_STRINGTEMP_LENGTH];
+       char string[VM_TEMPSTRING_MAXSIZE];
        int strindex, c, end;
        const char *filename;
        char vabuf[1024];
@@ -4290,7 +4360,7 @@ void VM_buf_loadfile(prvm_prog_t *prog)
                        c = FS_Getc(file);
                        if (c == '\r' || c == '\n' || c < 0)
                                break;
-                       if (end < VM_STRINGTEMP_LENGTH - 1)
+                       if (end < VM_TEMPSTRING_MAXSIZE - 1)
                                string[end++] = c;
                }
                string[end] = 0;
@@ -4494,7 +4564,7 @@ float bufstr_find(float bufhandle, string match, float matchrule, float startpos
 void VM_bufstr_find(prvm_prog_t *prog)
 {
        prvm_stringbuffer_t *stringbuffer;
-       char string[VM_STRINGTEMP_LENGTH];
+       char string[VM_TEMPSTRING_MAXSIZE];
        int matchrule, matchlen, i, step;
        const char *match;
 
@@ -4521,7 +4591,7 @@ void VM_bufstr_find(prvm_prog_t *prog)
                match = PRVM_G_STRING(OFS_PARM1);
        else
        {
-               strlcpy(string, PRVM_G_STRING(OFS_PARM1), sizeof(string));
+               dp_strlcpy(string, PRVM_G_STRING(OFS_PARM1), sizeof(string));
                match = detect_match_rule(string, &matchrule);
        }
        matchlen = (int)strlen(match);
@@ -4531,7 +4601,7 @@ void VM_bufstr_find(prvm_prog_t *prog)
        step = (prog->argc > 4) ? (int)PRVM_G_FLOAT(OFS_PARM4) : 1;
        while(i < stringbuffer->num_strings)
        {
-               if (stringbuffer->strings[i] && match_rule(stringbuffer->strings[i], VM_STRINGTEMP_LENGTH, match, matchlen, matchrule))
+               if (stringbuffer->strings[i] && match_rule(stringbuffer->strings[i], VM_TEMPSTRING_MAXSIZE, match, matchlen, matchrule))
                {
                        PRVM_G_FLOAT(OFS_RETURN) = i;
                        break;
@@ -4549,7 +4619,7 @@ float matchpattern(string s, string pattern, float matchrule, float startpos) =
 void VM_matchpattern(prvm_prog_t *prog)
 {
        const char *s, *match;
-       char string[VM_STRINGTEMP_LENGTH];
+       char string[VM_TEMPSTRING_MAXSIZE];
        int matchrule, l;
 
        VM_SAFEPARMCOUNTRANGE(2, 4, VM_matchpattern);
@@ -4567,7 +4637,7 @@ void VM_matchpattern(prvm_prog_t *prog)
                match = PRVM_G_STRING(OFS_PARM1);
        else
        {
-               strlcpy(string, PRVM_G_STRING(OFS_PARM1), sizeof(string));
+               dp_strlcpy(string, PRVM_G_STRING(OFS_PARM1), sizeof(string));
                match = detect_match_rule(string, &matchrule);
        }
 
@@ -4577,7 +4647,7 @@ void VM_matchpattern(prvm_prog_t *prog)
                s += max(0, min((unsigned int)PRVM_G_FLOAT(OFS_PARM3), strlen(s)-1));
 
        // match
-       PRVM_G_FLOAT(OFS_RETURN) = match_rule(s, VM_STRINGTEMP_LENGTH, match, l, matchrule);
+       PRVM_G_FLOAT(OFS_RETURN) = match_rule(s, VM_TEMPSTRING_MAXSIZE, match, l, matchrule);
 }
 
 /*
@@ -4786,15 +4856,15 @@ void VM_changepitch (prvm_prog_t *prog)
 
 void VM_uncolorstring (prvm_prog_t *prog)
 {
-       char szNewString[VM_STRINGTEMP_LENGTH];
+       char szNewString[VM_TEMPSTRING_MAXSIZE];
+       size_t szNewString_len;
        const char *szString;
 
        // Prepare Strings
        VM_SAFEPARMCOUNT(1, VM_uncolorstring);
        szString = PRVM_G_STRING(OFS_PARM0);
-       COM_StringDecolorize(szString, 0, szNewString, sizeof(szNewString), true);
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, szNewString);
-       
+       szNewString_len = COM_StringDecolorize(szString, 0, szNewString, sizeof(szNewString), true);
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, szNewString, szNewString_len);
 }
 
 // #221 float(string str, string sub[, float startpos]) strstrofs (FTE_STRINGS)
@@ -4859,11 +4929,12 @@ void VM_chr2str (prvm_prog_t *prog)
        char t[9 * 4 + 1];
        int i;
        size_t len = 0;
+
        VM_SAFEPARMCOUNTRANGE(0, 8, VM_chr2str);
        for(i = 0; i < prog->argc && len < sizeof(t)-1; ++i)
                len += u8_fromchar((Uchar)PRVM_G_FLOAT(OFS_PARM0+i*3), t + len, sizeof(t)-1);
-       t[len] = 0;
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, t);
+       t[len] = '\0';
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, t, len);
 }
 
 static int chrconv_number(int i, int base, int conv)
@@ -4950,8 +5021,10 @@ static int chrchar_alpha(int i, int basec, int baset, int convc, int convt, int
 //bulk convert a string. change case or colouring.
 void VM_strconv (prvm_prog_t *prog)
 {
-       int ccase, redalpha, rednum, len, i;
-       unsigned char resbuf[VM_STRINGTEMP_LENGTH];
+       int ccase, redalpha, rednum;
+       unsigned i;
+       size_t resbuf_len;
+       unsigned char resbuf[VM_TEMPSTRING_MAXSIZE];
        unsigned char *result = resbuf;
 
        VM_SAFEPARMCOUNTRANGE(3, 8, VM_strconv);
@@ -4959,10 +5032,9 @@ void VM_strconv (prvm_prog_t *prog)
        ccase = (int) PRVM_G_FLOAT(OFS_PARM0);  //0 same, 1 lower, 2 upper
        redalpha = (int) PRVM_G_FLOAT(OFS_PARM1);       //0 same, 1 white, 2 red,  5 alternate, 6 alternate-alternate
        rednum = (int) PRVM_G_FLOAT(OFS_PARM2); //0 same, 1 white, 2 red, 3 redspecial, 4 whitespecial, 5 alternate, 6 alternate-alternate
-       VM_VarString(prog, 3, (char *) resbuf, sizeof(resbuf));
-       len = (int)strlen((char *) resbuf);
+       resbuf_len = VM_VarString(prog, 3, (char *) resbuf, sizeof(resbuf));
 
-       for (i = 0; i < len; i++, result++)     //should this be done backwards?
+       for (i = 0; i < resbuf_len; i++, result++)      //should this be done backwards?
        {
                if (*result >= '0' && *result <= '9')   //normal numbers...
                        *result = chrconv_number(*result, '0', rednum);
@@ -4991,24 +5063,26 @@ void VM_strconv (prvm_prog_t *prog)
        }
        *result = '\0';
 
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, (char *) resbuf);
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, (char *)resbuf, result - resbuf);
 }
 
 // #225 string(float chars, string s, ...) strpad (FTE_STRINGS)
 void VM_strpad (prvm_prog_t *prog)
 {
-       char src[VM_STRINGTEMP_LENGTH];
-       char destbuf[VM_STRINGTEMP_LENGTH];
+       char src[VM_TEMPSTRING_MAXSIZE];
+       char destbuf[VM_TEMPSTRING_MAXSIZE];
+       size_t destbuf_len;
        int pad;
+
        VM_SAFEPARMCOUNTRANGE(1, 8, VM_strpad);
        pad = (int) PRVM_G_FLOAT(OFS_PARM0);
        VM_VarString(prog, 1, src, sizeof(src));
 
        // note: < 0 = left padding, > 0 = right padding,
        // this is reverse logic of printf!
-       dpsnprintf(destbuf, sizeof(destbuf), "%*s", -pad, src);
+       destbuf_len = dpsnprintf(destbuf, sizeof(destbuf), "%*s", -pad, src);
 
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, destbuf);
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, destbuf, destbuf_len);
 }
 
 // #226 string(string info, string key, string value, ...) infoadd (FTE_STRINGS)
@@ -5016,19 +5090,19 @@ void VM_strpad (prvm_prog_t *prog)
 void VM_infoadd (prvm_prog_t *prog)
 {
        const char *info, *key;
-       char value[VM_STRINGTEMP_LENGTH];
-       char temp[VM_STRINGTEMP_LENGTH];
+       char value[VM_TEMPSTRING_MAXSIZE];
+       char temp[VM_TEMPSTRING_MAXSIZE];
 
        VM_SAFEPARMCOUNTRANGE(2, 8, VM_infoadd);
        info = PRVM_G_STRING(OFS_PARM0);
        key = PRVM_G_STRING(OFS_PARM1);
        VM_VarString(prog, 2, value, sizeof(value));
 
-       strlcpy(temp, info, VM_STRINGTEMP_LENGTH);
+       dp_strlcpy(temp, info, VM_TEMPSTRING_MAXSIZE);
 
-       InfoString_SetValue(temp, VM_STRINGTEMP_LENGTH, key, value);
+       InfoString_SetValue(temp, VM_TEMPSTRING_MAXSIZE, key, value);
 
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, temp);
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, temp, strlen(temp));
 }
 
 // #227 string(string info, string key) infoget (FTE_STRINGS)
@@ -5037,15 +5111,15 @@ void VM_infoget (prvm_prog_t *prog)
 {
        const char *info;
        const char *key;
-       char value[VM_STRINGTEMP_LENGTH];
+       char value[VM_TEMPSTRING_MAXSIZE];
 
        VM_SAFEPARMCOUNT(2, VM_infoget);
        info = PRVM_G_STRING(OFS_PARM0);
        key = PRVM_G_STRING(OFS_PARM1);
 
-       InfoString_GetValue(info, key, value, VM_STRINGTEMP_LENGTH);
+       InfoString_GetValue(info, key, value, VM_TEMPSTRING_MAXSIZE);
 
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, value);
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, value, strlen(value));
 }
 
 //#228 float(string s1, string s2, float len) strncmp (FTE_STRINGS)
@@ -5088,11 +5162,13 @@ void VM_strncasecmp (prvm_prog_t *prog)
 void VM_crc16(prvm_prog_t *prog)
 {
        float insensitive;
-       char s[VM_STRINGTEMP_LENGTH];
+       char s[VM_TEMPSTRING_MAXSIZE];
+       size_t slen;
+
        VM_SAFEPARMCOUNTRANGE(2, 8, VM_crc16);
        insensitive = PRVM_G_FLOAT(OFS_PARM0);
-       VM_VarString(prog, 1, s, sizeof(s));
-       PRVM_G_FLOAT(OFS_RETURN) = (unsigned short) ((insensitive ? CRC_Block_CaseInsensitive : CRC_Block) ((unsigned char *) s, strlen(s)));
+       slen = VM_VarString(prog, 1, s, sizeof(s));
+       PRVM_G_FLOAT(OFS_RETURN) = (unsigned short) ((insensitive ? CRC_Block_CaseInsensitive : CRC_Block) ((unsigned char *) s, slen));
 }
 
 // #639 float(string digest, string data, ...) digest_hex
@@ -5104,15 +5180,14 @@ void VM_digest_hex(prvm_prog_t *prog)
        char outhex[65];
        int outlen;
 
-       char s[VM_STRINGTEMP_LENGTH];
-       int len;
+       char s[VM_TEMPSTRING_MAXSIZE];
+       size_t len;
 
        VM_SAFEPARMCOUNTRANGE(2, 8, VM_digest_hex);
        digest = PRVM_G_STRING(OFS_PARM0);
        if(!digest)
                digest = "";
-       VM_VarString(prog, 1, s, sizeof(s));
-       len = (int)strlen(s);
+       len = VM_VarString(prog, 1, s, sizeof(s));
 
        outlen = 0;
 
@@ -5137,8 +5212,8 @@ void VM_digest_hex(prvm_prog_t *prog)
                        outhex[2*i]   = hexmap[(out[i] >> 4) & 15];
                        outhex[2*i+1] = hexmap[(out[i] >> 0) & 15];
                }
-               outhex[2*i] = 0;
-               PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, outhex);
+               outhex[2*i] = '\0';
+               PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, outhex, 2*i);
        }
        else
                PRVM_G_INT(OFS_RETURN) = 0;
@@ -5164,7 +5239,7 @@ void VM_SetTraceGlobals(prvm_prog_t *prog, const trace_t *trace)
        PRVM_gameglobalfloat(trace_dpstartcontents) = trace->startsupercontents;
        PRVM_gameglobalfloat(trace_dphitcontents) = trace->hitsupercontents;
        PRVM_gameglobalfloat(trace_dphitq3surfaceflags) = trace->hitq3surfaceflags;
-       PRVM_gameglobalstring(trace_dphittexturename) = trace->hittexture ? PRVM_SetTempString(prog, trace->hittexture->name) : 0;
+       PRVM_gameglobalstring(trace_dphittexturename) = trace->hittexture ? PRVM_SetTempString(prog, trace->hittexture->name, strlen(trace->hittexture->name)) : 0;
 }
 
 void VM_ClearTraceGlobals(prvm_prog_t *prog)
@@ -5208,8 +5283,8 @@ void VM_Cmd_Reset(prvm_prog_t *prog)
 // does URI escaping on a string (replace evil stuff by %AB escapes)
 void VM_uri_escape (prvm_prog_t *prog)
 {
-       char src[VM_STRINGTEMP_LENGTH];
-       char dest[VM_STRINGTEMP_LENGTH];
+       char src[VM_TEMPSTRING_MAXSIZE];
+       char dest[VM_TEMPSTRING_MAXSIZE];
        char *p, *q;
        static const char *hex = "0123456789ABCDEF";
 
@@ -5232,17 +5307,17 @@ void VM_uri_escape (prvm_prog_t *prog)
                        *q++ = hex[ *(unsigned char *)p       & 0xF];
                }
        }
-       *q++ = 0;
+       *q = '\0';
 
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, dest);
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, dest, q - dest);
 }
 
 // #510 string(string input, ...) uri_unescape (DP_QC_URI_ESCAPE)
 // does URI unescaping on a string (get back the evil stuff)
 void VM_uri_unescape (prvm_prog_t *prog)
 {
-       char src[VM_STRINGTEMP_LENGTH];
-       char dest[VM_STRINGTEMP_LENGTH];
+       char src[VM_TEMPSTRING_MAXSIZE];
+       char dest[VM_TEMPSTRING_MAXSIZE];
        char *p, *q;
        int hi, lo;
 
@@ -5279,9 +5354,9 @@ nohex:
                // otherwise:
                *q++ = *p++;
        }
-       *q++ = 0;
+       *q = '\0';
 
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, dest);
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, dest, q - dest);
 }
 
 // #502 string(string filename) whichpack (DP_QC_WHICHPACK)
@@ -5294,7 +5369,7 @@ void VM_whichpack (prvm_prog_t *prog)
        fn = PRVM_G_STRING(OFS_PARM0);
        pack = FS_WhichPack(fn);
 
-       PRVM_G_INT(OFS_RETURN) = pack ? PRVM_SetTempString(prog, pack) : 0;
+       PRVM_G_INT(OFS_RETURN) = pack ? PRVM_SetTempString(prog, pack, strlen(pack)) : 0;
 }
 
 typedef struct
@@ -5332,11 +5407,11 @@ static void uri_to_string_callback(int status, size_t length_received, unsigned
        {
                if(length_received >= sizeof(handle->buffer))
                        length_received = sizeof(handle->buffer) - 1;
-               handle->buffer[length_received] = 0;
+               handle->buffer[length_received] = '\0';
 
                PRVM_G_FLOAT(OFS_PARM0) = handle->id;
                PRVM_G_FLOAT(OFS_PARM1) = status;
-               PRVM_G_INT(OFS_PARM2) = PRVM_SetTempString(prog, handle->buffer);
+               PRVM_G_INT(OFS_PARM2) = PRVM_SetTempString(prog, handle->buffer, length_received);
                prog->ExecuteProgram(prog, PRVM_allfunction(URI_Get_Callback), "QC function URI_Get_Callback is missing");
        }
 
@@ -5443,7 +5518,7 @@ void VM_uri_get (prvm_prog_t *prog)
                        // POST: we sign postdata \0 query string
                        size_t ll;
                        handle->sigdata = (char *)Z_Malloc(8192);
-                       strlcpy(handle->sigdata, "X-D0-Blind-ID-Detached-Signature: ", 8192);
+                       dp_strlcpy(handle->sigdata, "X-D0-Blind-ID-Detached-Signature: ", 8192);
                        l = strlen(handle->sigdata);
                        handle->siglen = Crypto_SignDataDetached(handle->postdata, handle->postlen + 1 + lq, postkeyid, handle->sigdata + l, 8192 - l);
                        if(!handle->siglen)
@@ -5463,7 +5538,7 @@ void VM_uri_get (prvm_prog_t *prog)
                        handle->sigdata[handle->siglen] = 0;
                }
 out1:
-               strlcpy(handle->posttype, posttype, sizeof(handle->posttype));
+               dp_strlcpy(handle->posttype, posttype, sizeof(handle->posttype));
                ret = Curl_Begin_ToMemory_POST(url, handle->sigdata, 0, handle->posttype, handle->postdata, handle->postlen, (unsigned char *) handle->buffer, sizeof(handle->buffer), uri_to_string_callback, handle);
        }
        else
@@ -5473,7 +5548,7 @@ out1:
                        // GET: we sign JUST the query string
                        size_t l, ll;
                        handle->sigdata = (char *)Z_Malloc(8192);
-                       strlcpy(handle->sigdata, "X-D0-Blind-ID-Detached-Signature: ", 8192);
+                       dp_strlcpy(handle->sigdata, "X-D0-Blind-ID-Detached-Signature: ", 8192);
                        l = strlen(handle->sigdata);
                        handle->siglen = Crypto_SignDataDetached(query_string, lq, postkeyid, handle->sigdata + l, 8192 - l);
                        if(!handle->siglen)
@@ -5516,6 +5591,7 @@ void VM_netaddress_resolve (prvm_prog_t *prog)
 {
        const char *ip;
        char normalized[128];
+       size_t normalized_len;
        int port;
        lhnetaddress_t addr;
 
@@ -5526,10 +5602,10 @@ void VM_netaddress_resolve (prvm_prog_t *prog)
        if(prog->argc > 1)
                port = (int) PRVM_G_FLOAT(OFS_PARM1);
 
-       if(LHNETADDRESS_FromString(&addr, ip, port) && LHNETADDRESS_ToString(&addr, normalized, sizeof(normalized), prog->argc > 1))
-               PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, normalized);
+       if(LHNETADDRESS_FromString(&addr, ip, port) && (normalized_len = LHNETADDRESS_ToString(&addr, normalized, sizeof(normalized), prog->argc > 1)))
+               PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, normalized, normalized_len);
        else
-               PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, "");
+               PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, "", 0);
 }
 
 //string(prvm_prog_t *prog) getextresponse = #624; // returns the next extResponse packet that was sent to this client
@@ -5544,7 +5620,7 @@ void VM_CL_getextresponse (prvm_prog_t *prog)
                int first;
                --cl_net_extresponse_count;
                first = (cl_net_extresponse_last + NET_EXTRESPONSE_MAX - cl_net_extresponse_count) % NET_EXTRESPONSE_MAX;
-               PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, cl_net_extresponse[first]);
+               PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, cl_net_extresponse[first], strlen(cl_net_extresponse[first]));
        }
 }
 
@@ -5559,7 +5635,42 @@ void VM_SV_getextresponse (prvm_prog_t *prog)
                int first;
                --sv_net_extresponse_count;
                first = (sv_net_extresponse_last + NET_EXTRESPONSE_MAX - sv_net_extresponse_count) % NET_EXTRESPONSE_MAX;
-               PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, sv_net_extresponse[first]);
+               PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, sv_net_extresponse[first], strlen(sv_net_extresponse[first]));
+       }
+}
+
+// DP_QC_NUDGEOUTOFSOLID
+// float(entity ent) nudgeoutofsolid = #567;
+void VM_nudgeoutofsolid(prvm_prog_t *prog)
+{
+       prvm_edict_t *ent;
+
+       VM_SAFEPARMCOUNTRANGE(1, 1, VM_nudgeoutofsolid);
+
+       ent = PRVM_G_EDICT(OFS_PARM0);
+       if (ent == prog->edicts)
+       {
+               VM_Warning(prog, "nudgeoutofsolid: can not modify world entity\n");
+               PRVM_G_FLOAT(OFS_RETURN) = 0;
+               return;
+       }
+       if (ent->free)
+       {
+               VM_Warning(prog, "nudgeoutofsolid: can not modify free entity\n");
+               PRVM_G_FLOAT(OFS_RETURN) = 0;
+               return;
+       }
+
+       PRVM_G_FLOAT(OFS_RETURN) = PHYS_NudgeOutOfSolid(prog, ent);
+
+       if (PRVM_G_FLOAT(OFS_RETURN) > 0)
+       {
+               if (prog == SVVM_prog)
+                       SV_LinkEdict(ent);
+               else if (prog == CLVM_prog)
+                       CL_LinkEdict(ent);
+               else
+                       Sys_Error("PHYS_NudgeOutOfSolid: cannot be called from %s VM\n", prog->name);
        }
 }
 
@@ -5977,9 +6088,10 @@ verbatim:
                                break;
                }
        }
+
 finished:
-       *o = 0;
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, outbuf);
+       *o = '\0';
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, outbuf, o - outbuf);
 }
 
 
@@ -6305,11 +6417,12 @@ void VM_getsurfacetexture(prvm_prog_t *prog)
 {
        model_t *model;
        msurface_t *surface;
+
        VM_SAFEPARMCOUNT(2, VM_getsurfacetexture);
        PRVM_G_INT(OFS_RETURN) = OFS_NULL;
        if (!(model = getmodel(prog, PRVM_G_EDICT(OFS_PARM0))) || !(surface = getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1))))
                return;
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, surface->texture->name);
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, surface->texture->name, strlen(surface->texture->name));
 }
 //PF_getsurfacenearpoint, // #438 float(entity e, vector p) getsurfacenearpoint = #438;
 void VM_getsurfacenearpoint(prvm_prog_t *prog)
index a5555cde87db8ca68f0fb71380fcc4ada18f1842..292e8f556b7da7c0b1d4c84f2f447c1a6f63a5d2 100644 (file)
@@ -212,13 +212,14 @@ float     getserverlistindexforkey(string key)
 
 #define        VM_RETURN_EDICT(e)              (prog->globals.ip[OFS_RETURN] = PRVM_EDICT_TO_PROG(e))
 
-#define VM_STRINGTEMP_LENGTH MAX_INPUTLINE
+#define VM_TEMPSTRING_MAXSIZE MAX_INPUTLINE
 
 // general functions
 void VM_CheckEmptyString (prvm_prog_t *prog, const char *s);
-void VM_VarString(prvm_prog_t *prog, int first, char *out, int outlength);
-qbool PRVM_ConsoleCommand (prvm_prog_t *prog, const char *text, int *func, qbool preserve_self, int curself, double ptime, qbool prog_loaded, const char *error_message);
-prvm_stringbuffer_t *BufStr_FindCreateReplace (prvm_prog_t *prog, int bufindex, int flags, const char *format);
+/// Returns the length of the *out string excluding the \0 terminator.
+size_t VM_VarString(prvm_prog_t *prog, int first, char *out, size_t outsize);
+qbool PRVM_ConsoleCommand(prvm_prog_t *prog, const char *text, size_t textlen, int *func, qbool preserve_self, int curself, double ptime, const char *error_message);
+prvm_stringbuffer_t *BufStr_FindCreateReplace (prvm_prog_t *prog, int bufindex, unsigned flags, const char *format);
 void BufStr_Set(prvm_prog_t *prog, prvm_stringbuffer_t *stringbuffer, int strindex, const char *str);
 void BufStr_Del(prvm_prog_t *prog, prvm_stringbuffer_t *stringbuffer);
 void BufStr_Flush(prvm_prog_t *prog);
@@ -238,8 +239,7 @@ void VM_vectoangles (prvm_prog_t *prog);
 void VM_random (prvm_prog_t *prog);
 void VM_localsound(prvm_prog_t *prog);
 void VM_break (prvm_prog_t *prog);
-void VM_localcmd_local(prvm_prog_t *prog);
-void VM_localcmd_server(prvm_prog_t *prog);
+void VM_localcmd(prvm_prog_t *prog);
 void VM_cvar (prvm_prog_t *prog);
 void VM_cvar_string(prvm_prog_t *prog);
 void VM_cvar_type (prvm_prog_t *prog);
@@ -484,6 +484,7 @@ void VM_getsurfacetriangle(prvm_prog_t *prog);
 void VM_physics_enable(prvm_prog_t *prog);
 void VM_physics_addforce(prvm_prog_t *prog);
 void VM_physics_addtorque(prvm_prog_t *prog);
+void VM_nudgeoutofsolid(prvm_prog_t *prog);
 
 void VM_coverage(prvm_prog_t *prog);
 
index ea8392751e9760f2aff73c0881557e1f7c608557..8b847f7be41ebf21044b3061aea0a99c3177db5a 100644 (file)
@@ -22,6 +22,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include "quakedef.h"
 #include "progsvm.h"
 #include "csprogs.h"
+#include "prvm_cmds.h"
 
 prvm_prog_t prvm_prog_list[PRVM_PROG_MAX];
 
@@ -45,8 +46,11 @@ cvar_t prvm_breakpointdump = {CF_CLIENT | CF_SERVER, "prvm_breakpointdump", "0",
 cvar_t prvm_reuseedicts_startuptime = {CF_CLIENT | CF_SERVER, "prvm_reuseedicts_startuptime", "2", "allows immediate re-use of freed entity slots during start of new level (value in seconds)"};
 cvar_t prvm_reuseedicts_neverinsameframe = {CF_CLIENT | CF_SERVER, "prvm_reuseedicts_neverinsameframe", "1", "never allows re-use of freed entity slots during same frame"};
 cvar_t prvm_garbagecollection_enable = {CF_CLIENT | CF_SERVER, "prvm_garbagecollection_enable", "1", "automatically scan for and free resources that are not referenced by the code being executed in the VM"};
-cvar_t prvm_garbagecollection_notify = {CF_CLIENT | CF_SERVER, "prvm_garbagecollection_notify", "0", "print out a notification for each resource freed by garbage collection"};
-cvar_t prvm_garbagecollection_scan_limit = {CF_CLIENT | CF_SERVER, "prvm_garbagecollection_scan_limit", "10000", "scan this many fields or resources per frame to free up unreferenced resources"};
+cvar_t prvm_garbagecollection_notify = {CF_CLIENT | CF_SERVER, "prvm_garbagecollection_notify", "0", "print out a notification for each resource freed by garbage collection (set developer >= 1 to see these)"};
+/// At 50k in Xonotic with 8 bots scans take about: 24s server, 25s menu, 9s client.
+/// At 50k in Quake 1.5: 2.2s server, 0.14s client.
+/// At 50k impact on high FPS benchmarks is negligible, at 100k impact is low but measurable.
+cvar_t prvm_garbagecollection_scan_limit = {CF_CLIENT | CF_SERVER, "prvm_garbagecollection_scan_limit", "50000", "scan this many fields or resources per second to free up unreferenced resources"};
 cvar_t prvm_garbagecollection_strings = {CF_CLIENT | CF_SERVER, "prvm_garbagecollection_strings", "1", "automatically call strunzone() on strings that are not referenced"};
 cvar_t prvm_stringdebug = {CF_CLIENT | CF_SERVER, "prvm_stringdebug", "0", "Print debug and warning messages related to strings"};
 
@@ -220,8 +224,9 @@ const char *PRVM_AllocationOrigin(prvm_prog_t *prog)
        if(prog->leaktest_active)
        if(prog->depth > 0) // actually in QC code and not just parsing the entities block of a map/savegame
        {
-               buf = (char *)PRVM_Alloc(256);
-               PRVM_ShortStackTrace(prog, buf, 256);
+               // bones_was_here: this is the smallest 64 multiple that avoids truncation in Xonotic (was 256)
+               buf = (char *)PRVM_Alloc(448);
+               PRVM_ShortStackTrace(prog, buf, 448);
        }
        return buf;
 }
@@ -297,7 +302,11 @@ prvm_edict_t *PRVM_ED_Alloc(prvm_prog_t *prog)
 PRVM_ED_Free
 
 Marks the edict as free
+
 FIXME: walk all entities and NULL out references to this entity
+bones_was_here: do not want, that would break chains immediately!
+Currently chains aren't broken by removing an entity, at least with prvm_reuseedicts_neverinsameframe 1
+which is very handy and some QC code will depend on it.
 =================
 */
 void PRVM_ED_Free(prvm_prog_t *prog, prvm_edict_t *ed)
@@ -444,7 +453,7 @@ static char *PRVM_ValueString (prvm_prog_t *prog, etype_t type, prvm_eval_t *val
        switch (type)
        {
        case ev_string:
-               strlcpy (line, PRVM_GetString (prog, val->string), linelength);
+               dp_strlcpy (line, PRVM_GetString (prog, val->string), linelength);
                break;
        case ev_entity:
                n = val->edict;
@@ -551,7 +560,7 @@ char *PRVM_UglyValueString (prvm_prog_t *prog, etype_t type, prvm_eval_t *val, c
                if ((unsigned int)val->function < (unsigned int)prog->progs_numfunctions)
                {
                        f = prog->functions + val->function;
-                       strlcpy (line, PRVM_GetString (prog, f->s_name), linelength);
+                       dp_strlcpy (line, PRVM_GetString (prog, f->s_name), linelength);
                }
                else
                        dpsnprintf (line, linelength, "bad function %" PRVM_PRIi " (invalid!)", val->function);
@@ -693,10 +702,10 @@ void PRVM_ED_Print(prvm_prog_t *prog, prvm_edict_t *ed, const char *wildcard_fie
                        tempstring2[sizeof(tempstring2)-1] = 0;
                        name = tempstring2;
                }
-               strlcat(tempstring, name, sizeof(tempstring));
+               dp_strlcat(tempstring, name, sizeof(tempstring));
                for (l = strlen(name);l < 14;l++)
-                       strlcat(tempstring, " ", sizeof(tempstring));
-               strlcat(tempstring, " ", sizeof(tempstring));
+                       dp_strlcat(tempstring, " ", sizeof(tempstring));
+               dp_strlcat(tempstring, " ", sizeof(tempstring));
 
                name = PRVM_ValueString(prog, (etype_t)d->type, val, valuebuf, sizeof(valuebuf));
                if (strlen(name) > sizeof(tempstring2)-4)
@@ -706,8 +715,8 @@ void PRVM_ED_Print(prvm_prog_t *prog, prvm_edict_t *ed, const char *wildcard_fie
                        tempstring2[sizeof(tempstring2)-1] = 0;
                        name = tempstring2;
                }
-               strlcat(tempstring, name, sizeof(tempstring));
-               strlcat(tempstring, "\n", sizeof(tempstring));
+               dp_strlcat(tempstring, name, sizeof(tempstring));
+               dp_strlcat(tempstring, "\n", sizeof(tempstring));
                if (strlen(tempstring) >= sizeof(tempstring)/2)
                {
                        Con_Print(tempstring);
@@ -942,7 +951,7 @@ void PRVM_ED_ParseGlobals (prvm_prog_t *prog, const char *data)
                if (developer_entityparsing.integer)
                        Con_Printf("Key: \"%s\"", com_token);
 
-               strlcpy (keyname, com_token, sizeof(keyname));
+               dp_strlcpy (keyname, com_token, sizeof(keyname));
 
                // parse value
                if (!COM_ParseToken_Simple(&data, false, true, true))
@@ -1118,7 +1127,7 @@ static void PRVM_GameCommand(cmd_state_t *cmd, const char *whichprogs, const cha
                s = Cmd_Args(cmd);
 
                restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
-               PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(prog, s ? s : "");
+               PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(prog, s ? s : "", s ? strlen(s) : 0);
                prog->ExecuteProgram(prog, PRVM_allfunction(GameCommand), "QC function GameCommand is missing");
                prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
        }
@@ -1292,7 +1301,7 @@ const char *PRVM_ED_ParseEdict (prvm_prog_t *prog, const char *data, prvm_edict_
                // and allow them to be turned into vectors. (FIXME...)
                if (!strcmp(com_token, "angle"))
                {
-                       strlcpy (com_token, "angles", sizeof(com_token));
+                       dp_strlcpy (com_token, "angles", sizeof(com_token));
                        anglehack = true;
                }
                else
@@ -1300,9 +1309,9 @@ const char *PRVM_ED_ParseEdict (prvm_prog_t *prog, const char *data, prvm_edict_
 
                // FIXME: change light to _light to get rid of this hack
                if (!strcmp(com_token, "light"))
-                       strlcpy (com_token, "light_lev", sizeof(com_token));    // hack for single light def
+                       dp_strlcpy (com_token, "light_lev", sizeof(com_token)); // hack for single light def
 
-               strlcpy (keyname, com_token, sizeof(keyname));
+               dp_strlcpy (keyname, com_token, sizeof(keyname));
 
                // another hack to fix keynames with trailing spaces
                n = strlen(keyname);
@@ -1342,7 +1351,7 @@ const char *PRVM_ED_ParseEdict (prvm_prog_t *prog, const char *data, prvm_edict_
                if (anglehack)
                {
                        char    temp[32];
-                       strlcpy (temp, com_token, sizeof(temp));
+                       dp_strlcpy (temp, com_token, sizeof(temp));
                        dpsnprintf (com_token, sizeof(com_token), "0 %s 0", temp);
                }
 
@@ -1848,7 +1857,8 @@ static po_t *PRVM_PO_Load(const char *filename, const char *filename2, mempool_t
                                        break;
                                if((size_t)(q - p) >= (size_t) sizeof(inbuf))
                                        break;
-                               strlcpy(inbuf, p, q - p); // not - 1, because this adds a NUL
+                               memcpy(inbuf, p, q - p - 1);
+                               inbuf[q - p - 1] = '\0';
                                PRVM_PO_ParseString(decodedbuf + decodedpos, inbuf, sizeof(decodedbuf) - decodedpos);
                                decodedpos += strlen(decodedbuf + decodedpos);
                                if(*q == '\r')
@@ -1943,7 +1953,7 @@ static void PRVM_LoadLNO( prvm_prog_t *prog, const char *progname ) {
        char filename[512];
 
        FS_StripExtension( progname, filename, sizeof( filename ) );
-       strlcat( filename, ".lno", sizeof( filename ) );
+       dp_strlcat( filename, ".lno", sizeof( filename ) );
 
        lno = FS_LoadFile( filename, tempmempool, false, &filesize );
        if( !lno ) {
@@ -1966,12 +1976,12 @@ static void PRVM_LoadLNO( prvm_prog_t *prog, const char *progname ) {
        }
 
        header = (unsigned int *) lno;
-       if( header[ 0 ] == *(unsigned int *) "LNOF" &&
-               LittleLong( header[ 1 ] ) == 1 &&
-               (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs_numglobaldefs &&
-               (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs_numglobals &&
-               (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs_numfielddefs &&
-               (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs_numstatements )
+       if (memcmp(lno, "LNOF", 4) == 0
+       && LittleLong( header[ 1 ] ) == 1
+       && (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs_numglobaldefs
+       && (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs_numglobals
+       && (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs_numfielddefs
+       && (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs_numstatements)
        {
                prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof( int ) );
                memcpy( prog->statement_linenums, header + 6, prog->progs_numstatements * sizeof( int ) );
@@ -1988,11 +1998,11 @@ static void PRVM_LoadLNO( prvm_prog_t *prog, const char *progname ) {
 
 /*
 ===============
-PRVM_LoadProgs
+PRVM_Prog_Load
 ===============
 */
 static void PRVM_UpdateBreakpoints(prvm_prog_t *prog);
-void PRVM_Prog_Load(prvm_prog_t *prog, const char * filename, unsigned char * data, fs_offset_t size, int numrequiredfunc, const char **required_func, int numrequiredfields, prvm_required_field_t *required_field, int numrequiredglobals, prvm_required_field_t *required_global)
+void PRVM_Prog_Load(prvm_prog_t *prog, const char *filename, unsigned char *data, fs_offset_t size, void CheckRequiredFuncs(prvm_prog_t *prog, const char *filename), int numrequiredfields, prvm_required_field_t *required_field, int numrequiredglobals, prvm_required_field_t *required_global)
 {
        int i;
        dprograms_t *dprograms;
@@ -2026,7 +2036,7 @@ void PRVM_Prog_Load(prvm_prog_t *prog, const char * filename, unsigned char * da
        int structtype = 0;
 
        if (prog->loaded)
-               prog->error_cmd("PRVM_LoadProgs: there is already a %s program loaded!", prog->name );
+               prog->error_cmd("%s: there is already a %s program loaded!", __func__, prog->name);
 
        Host_LockSession(); // all progs can use the session cvar
        Crypto_LoadKeys(); // all progs might use the keys at init time
@@ -2039,7 +2049,7 @@ void PRVM_Prog_Load(prvm_prog_t *prog, const char * filename, unsigned char * da
        else
                dprograms = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
        if (dprograms == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
-               prog->error_cmd("PRVM_LoadProgs: couldn't load %s for %s", filename, prog->name);
+               prog->error_cmd("%s: couldn't load \"%s\" for %s", __func__, filename, prog->name);
        // TODO bounds check header fields (e.g. numstatements), they must never go behind end of file
 
        prog->profiletime = Sys_DirtyTime();
@@ -2132,7 +2142,7 @@ void PRVM_Prog_Load(prvm_prog_t *prog, const char * filename, unsigned char * da
                prog->functions[i].locals = LittleLong(infunctions[i].locals);
                memcpy(prog->functions[i].parm_size, infunctions[i].parm_size, sizeof(infunctions[i].parm_size));
                if(prog->functions[i].first_statement >= prog->numstatements)
-                       prog->error_cmd("PRVM_LoadProgs: out of bounds function statement (function %d) in %s", i, prog->name);
+                       prog->error_cmd("%s: out of bounds function statement (function %d) in %s", __func__, i, prog->name);
                // TODO bounds check parm_start, s_name, s_file, numparms, locals, parm_size
        }
 
@@ -2180,7 +2190,7 @@ void PRVM_Prog_Load(prvm_prog_t *prog, const char * filename, unsigned char * da
                {
                        prog->fielddefs[i].type = LittleLong(infielddefs32[i].type);
                        if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
-                               prog->error_cmd("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", prog->name);
+                               prog->error_cmd("%s: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", __func__, prog->name);
                        prog->fielddefs[i].ofs = LittleLong(infielddefs32[i].ofs);
                        prog->fielddefs[i].s_name = LittleLong(infielddefs32[i].s_name);
                        // TODO bounds check ofs, s_name
@@ -2191,7 +2201,7 @@ void PRVM_Prog_Load(prvm_prog_t *prog, const char * filename, unsigned char * da
                {
                        prog->fielddefs[i].type = (unsigned short)LittleShort(infielddefs16[i].type);
                        if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
-                               prog->error_cmd("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", prog->name);
+                               prog->error_cmd("%s: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", __func__, prog->name);
                        prog->fielddefs[i].ofs = (unsigned short)LittleShort(infielddefs16[i].ofs);
                        prog->fielddefs[i].s_name = LittleLong(infielddefs16[i].s_name);
                        // TODO bounds check ofs, s_name
@@ -2263,7 +2273,7 @@ void PRVM_Prog_Load(prvm_prog_t *prog, const char * filename, unsigned char * da
                case OP_IFNOT:
                        b = (short)b;
                        if (a >= prog->progs_numglobals || b + i < 0 || b + i >= prog->progs_numstatements)
-                               prog->error_cmd("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, prog->name);
+                               prog->error_cmd("%s: out of bounds IF/IFNOT (statement %d) in %s", __func__, i, prog->name);
                        prog->statements[i].op = op;
                        prog->statements[i].operand[0] = remapglobal(a);
                        prog->statements[i].operand[1] = -1;
@@ -2273,7 +2283,7 @@ void PRVM_Prog_Load(prvm_prog_t *prog, const char * filename, unsigned char * da
                case OP_GOTO:
                        a = (short)a;
                        if (a + i < 0 || a + i >= prog->progs_numstatements)
-                               prog->error_cmd("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, prog->name);
+                               prog->error_cmd("%s: out of bounds GOTO (statement %d) in %s", __func__, i, prog->name);
                        prog->statements[i].op = op;
                        prog->statements[i].operand[0] = -1;
                        prog->statements[i].operand[1] = -1;
@@ -2281,7 +2291,7 @@ void PRVM_Prog_Load(prvm_prog_t *prog, const char * filename, unsigned char * da
                        prog->statements[i].jumpabsolute = i + a;
                        break;
                default:
-                       Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", (int)op, i, prog->name);
+                       Con_DPrintf("%s: unknown opcode %d at statement %d in %s\n", __func__, (int)op, i, prog->name);
 
                        //make sure its something well defined.
                        prog->statements[i].op = OP_BOUNDCHECK;
@@ -2394,7 +2404,7 @@ void PRVM_Prog_Load(prvm_prog_t *prog, const char * filename, unsigned char * da
                case OP_LOAD_FNC:
                case OP_LOAD_V:
                        if (a >= prog->progs_numglobals || b >= prog->progs_numglobals || c >= prog->progs_numglobals)
-                               prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
+                               prog->error_cmd("%s: out of bounds global index (statement %d)", __func__, i);
                        prog->statements[i].op = op;
                        prog->statements[i].operand[0] = remapglobal(a);
                        prog->statements[i].operand[1] = remapglobal(b);
@@ -2408,7 +2418,7 @@ void PRVM_Prog_Load(prvm_prog_t *prog, const char * filename, unsigned char * da
                case OP_NOT_FNC:
                case OP_NOT_ENT:
                        if (a >= prog->progs_numglobals || c >= prog->progs_numglobals)
-                               prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
+                               prog->error_cmd("%s: out of bounds global index (statement %d) in %s", __func__, i, prog->name);
                        prog->statements[i].op = op;
                        prog->statements[i].operand[0] = remapglobal(a);
                        prog->statements[i].operand[1] = -1;
@@ -2422,7 +2432,7 @@ void PRVM_Prog_Load(prvm_prog_t *prog, const char * filename, unsigned char * da
                case OP_STOREP_S:
                case OP_STOREP_FNC:
                        if (c)  //Spike -- DP is alergic to pointers in QC. Try to avoid too many nasty surprises.
-                               Con_DPrintf("PRVM_LoadProgs: storep-with-offset is not permitted in %s\n", prog->name);
+                               Con_DPrintf("%s: storep-with-offset is not permitted in %s\n", __func__, prog->name);
                        //fallthrough
                case OP_STORE_F:
                case OP_STORE_ENT:
@@ -2433,7 +2443,7 @@ void PRVM_Prog_Load(prvm_prog_t *prog, const char * filename, unsigned char * da
                case OP_STOREP_V:
                case OP_STORE_V:
                        if (a >= prog->progs_numglobals || b >= prog->progs_numglobals)
-                               prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
+                               prog->error_cmd("%s: out of bounds global index (statement %d) in %s", __func__, i, prog->name);
                        prog->statements[i].op = op;
                        prog->statements[i].operand[0] = remapglobal(a);
                        prog->statements[i].operand[1] = remapglobal(b);
@@ -2458,9 +2468,9 @@ void PRVM_Prog_Load(prvm_prog_t *prog, const char * filename, unsigned char * da
                case OP_DONE:
                case OP_RETURN:
                        if ( a >= prog->progs_numglobals)
-                               prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
+                               prog->error_cmd("%s: out of bounds global index (statement %d) in %s", __func__, i, prog->name);
                        if (b || c)     //Spike -- added this check just as a diagnostic...
-                               Con_DPrintf("PRVM_LoadProgs: unexpected offset on call opcode in %s. Hexen2 format is not supported\n", prog->name);
+                               Con_DPrintf("%s: unexpected offset on call opcode in %s. Hexen2 format is not supported\n", __func__, prog->name);
                        prog->statements[i].op = op;
                        prog->statements[i].operand[0] = remapglobal(a);
                        prog->statements[i].operand[1] = -1;
@@ -2471,7 +2481,7 @@ void PRVM_Prog_Load(prvm_prog_t *prog, const char * filename, unsigned char * da
        }
        if(prog->numstatements < 1)
        {
-               prog->error_cmd("PRVM_LoadProgs: empty program in %s", prog->name);
+               prog->error_cmd("%s: empty program in %s", __func__, prog->name);
        }
        else switch(prog->statements[prog->numstatements - 1].op)
        {
@@ -2480,7 +2490,7 @@ void PRVM_Prog_Load(prvm_prog_t *prog, const char * filename, unsigned char * da
                case OP_DONE:
                        break;
                default:
-                       prog->error_cmd("PRVM_LoadProgs: program may fall off the edge (does not end with RETURN, GOTO or DONE) in %s", prog->name);
+                       prog->error_cmd("%s: program may fall off the edge (does not end with RETURN, GOTO or DONE) in %s", __func__, prog->name);
                        break;
        }
 
@@ -2489,10 +2499,9 @@ void PRVM_Prog_Load(prvm_prog_t *prog, const char * filename, unsigned char * da
                Mem_Free(dprograms);
        dprograms = NULL;
 
-       // check required functions
-       for(i=0 ; i < numrequiredfunc ; i++)
-               if(PRVM_ED_FindFunction(prog, required_func[i]) == 0)
-                       prog->error_cmd("%s: %s not found in %s",prog->name, required_func[i], filename);
+       prog->flag = 0;
+       // expected to not return (call prog->error_cmd) if checks fail
+       CheckRequiredFuncs(prog, filename);
 
        PRVM_LoadLNO(prog, filename);
 
@@ -2588,14 +2597,14 @@ void PRVM_Prog_Load(prvm_prog_t *prog, const char * filename, unsigned char * da
                {
                        prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
                        cvar = Cvar_FindVar(prog->console_cmd->cvars, name + 9, prog->console_cmd->cvars_flagsmask);
-                       //Con_Printf("PRVM_LoadProgs: autocvar global %s in %s, processing...\n", name, prog->name);
+                       //Con_Printf("%s: autocvar global %s in %s, processing...\n", __func__, name, prog->name);
                        if(!cvar)
                        {
                                const char *value;
                                char buf[128];
                                int prec[3];
                                float f;
-                               Con_DPrintf("PRVM_LoadProgs: no cvar for autocvar global %s in %s, creating...\n", name, prog->name);
+                               Con_DPrintf("%s: no cvar for autocvar global %s in %s, creating...\n", __func__, name, prog->name);
                                switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
                                {
                                        case ev_float:
@@ -2634,7 +2643,7 @@ void PRVM_Prog_Load(prvm_prog_t *prog, const char * filename, unsigned char * da
                                                value = PRVM_GetString(prog, val->string);
                                                break;
                                        default:
-                                               Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
+                                               Con_Printf("%s: invalid type of autocvar global %s in %s\n", __func__, name, prog->name);
                                                goto fail;
                                }
                                cvar = Cvar_Get(prog->console_cmd->cvars, name + 9, value, prog->console_cmd->cvars_flagsmask, NULL);
@@ -2644,7 +2653,7 @@ void PRVM_Prog_Load(prvm_prog_t *prog, const char * filename, unsigned char * da
                                        cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
                                }
                                if(!cvar)
-                                       prog->error_cmd("PRVM_LoadProgs: could not create cvar for autocvar global %s in %s", name, prog->name);
+                                       prog->error_cmd("%s: could not create cvar for autocvar global %s in %s", __func__, name, prog->name);
                                cvar->globaldefindex[prog - prvm_prog_list] = i;
                        }
                        else if((cvar->flags & CF_PRIVATE) == 0)
@@ -2678,13 +2687,13 @@ void PRVM_Prog_Load(prvm_prog_t *prog, const char * filename, unsigned char * da
                                                cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
                                                break;
                                        default:
-                                               Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
+                                               Con_Printf("%s: invalid type of autocvar global %s in %s\n", __func__, name, prog->name);
                                                goto fail;
                                }
                                cvar->globaldefindex[prog - prvm_prog_list] = i;
                        }
                        else
-                               Con_Printf("PRVM_LoadProgs: private cvar for autocvar global %s in %s\n", name, prog->name);
+                               Con_Printf("%s: private cvar for autocvar global %s in %s\n", __func__, name, prog->name);
                }
 fail:
                ;
@@ -2696,8 +2705,6 @@ fail:
 
        // set flags & mdef_ts in prog
 
-       prog->flag = 0;
-
        PRVM_FindOffsets(prog);
 
        prog->init_cmd(prog);
@@ -2778,32 +2785,32 @@ static void PRVM_Fields_f(cmd_state_t *cmd)
                switch(d->type & ~DEF_SAVEGLOBAL)
                {
                case ev_string:
-                       strlcat(tempstring, "string   ", sizeof(tempstring));
+                       dp_strlcat(tempstring, "string   ", sizeof(tempstring));
                        break;
                case ev_entity:
-                       strlcat(tempstring, "entity   ", sizeof(tempstring));
+                       dp_strlcat(tempstring, "entity   ", sizeof(tempstring));
                        break;
                case ev_function:
-                       strlcat(tempstring, "function ", sizeof(tempstring));
+                       dp_strlcat(tempstring, "function ", sizeof(tempstring));
                        break;
                case ev_field:
-                       strlcat(tempstring, "field    ", sizeof(tempstring));
+                       dp_strlcat(tempstring, "field    ", sizeof(tempstring));
                        break;
                case ev_void:
-                       strlcat(tempstring, "void     ", sizeof(tempstring));
+                       dp_strlcat(tempstring, "void     ", sizeof(tempstring));
                        break;
                case ev_float:
-                       strlcat(tempstring, "float    ", sizeof(tempstring));
+                       dp_strlcat(tempstring, "float    ", sizeof(tempstring));
                        break;
                case ev_vector:
-                       strlcat(tempstring, "vector   ", sizeof(tempstring));
+                       dp_strlcat(tempstring, "vector   ", sizeof(tempstring));
                        break;
                case ev_pointer:
-                       strlcat(tempstring, "pointer  ", sizeof(tempstring));
+                       dp_strlcat(tempstring, "pointer  ", sizeof(tempstring));
                        break;
                default:
                        dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
-                       strlcat(tempstring, tempstring2, sizeof(tempstring));
+                       dp_strlcat(tempstring, tempstring2, sizeof(tempstring));
                        break;
                }
                if (strlen(name) > sizeof(tempstring2)-4)
@@ -2813,12 +2820,12 @@ static void PRVM_Fields_f(cmd_state_t *cmd)
                        tempstring2[sizeof(tempstring2)-1] = 0;
                        name = tempstring2;
                }
-               strlcat(tempstring, name, sizeof(tempstring));
+               dp_strlcat(tempstring, name, sizeof(tempstring));
                for (j = (int)strlen(name);j < 25;j++)
-                       strlcat(tempstring, " ", sizeof(tempstring));
+                       dp_strlcat(tempstring, " ", sizeof(tempstring));
                dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
-               strlcat(tempstring, tempstring2, sizeof(tempstring));
-               strlcat(tempstring, "\n", sizeof(tempstring));
+               dp_strlcat(tempstring, tempstring2, sizeof(tempstring));
+               dp_strlcat(tempstring, "\n", sizeof(tempstring));
                if (strlen(tempstring) >= sizeof(tempstring)/2)
                {
                        Con_Print(tempstring);
@@ -3069,7 +3076,7 @@ static void PRVM_Breakpoint_f(cmd_state_t *cmd)
 
        {
                debug_data_t *debug = &debug_data[prog - prvm_prog_list];
-               strlcpy(debug->break_statement, Cmd_Argv(cmd, 2), sizeof(debug->break_statement));
+               dp_strlcpy(debug->break_statement, Cmd_Argv(cmd, 2), sizeof(debug->break_statement));
        }
        PRVM_UpdateBreakpoints(prog);
 }
@@ -3098,7 +3105,7 @@ static void PRVM_GlobalWatchpoint_f(cmd_state_t *cmd)
 
        {
                debug_data_t *debug = &debug_data[prog - prvm_prog_list];
-               strlcpy(debug->watch_global, Cmd_Argv(cmd, 2), sizeof(debug->watch_global));
+               dp_strlcpy(debug->watch_global, Cmd_Argv(cmd, 2), sizeof(debug->watch_global));
        }
        PRVM_UpdateBreakpoints(prog);
 }
@@ -3128,7 +3135,7 @@ static void PRVM_EdictWatchpoint_f(cmd_state_t *cmd)
        {
                debug_data_t *debug = &debug_data[prog - prvm_prog_list];
                debug->watch_edict = atoi(Cmd_Argv(cmd, 2));
-               strlcpy(debug->watch_field, Cmd_Argv(cmd, 3), sizeof(debug->watch_field));
+               dp_strlcpy(debug->watch_field, Cmd_Argv(cmd, 3), sizeof(debug->watch_field));
        }
        PRVM_UpdateBreakpoints(prog);
 }
@@ -3140,6 +3147,8 @@ PRVM_Init
 */
 void PRVM_Init (void)
 {
+       unsigned int i;
+
        Cmd_AddCommand(CF_SHARED, "prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
        Cmd_AddCommand(CF_SHARED, "prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
        Cmd_AddCommand(CF_SHARED, "prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
@@ -3184,6 +3193,18 @@ void PRVM_Init (void)
        prvm_runawaycheck = !Sys_CheckParm("-norunaway");
 
        //VM_Cmd_Init();
+
+       // LadyHavoc: report supported extensions
+       Con_DPrintf("\nQuakeC extensions for server and client:");
+       for (i = 0; vm_sv_extensions[i]; i++)
+               Con_DPrintf(" %s", vm_sv_extensions[i]);
+       Con_DPrintf("\n");
+#ifdef CONFIG_MENU
+       Con_DPrintf("\nQuakeC extensions for menu:");
+       for (i = 0; vm_m_extensions[i]; i++)
+               Con_DPrintf(" %s", vm_m_extensions[i]);
+       Con_DPrintf("\n");
+#endif
 }
 
 /*
@@ -3347,27 +3368,27 @@ int PRVM_SetEngineString(prvm_prog_t *prog, const char *s)
 //  restores it on return, so multiple recursive calls can share the same
 //  buffer)
 // the buffer size is automatically grown as needed
-
-int PRVM_SetTempString(prvm_prog_t *prog, const char *s)
+int PRVM_SetTempString(prvm_prog_t *prog, const char *s, size_t slen)
 {
-       int size;
+       size_t size;
        char *t;
-       if (!s)
+
+       if (!s || slen >= VM_TEMPSTRING_MAXSIZE)
                return 0;
-       size = (int)strlen(s) + 1;
+       size = slen + 1;
        if (developer_insane.integer)
-               Con_DPrintf("PRVM_SetTempString: cursize %i, size %i\n", prog->tempstringsbuf.cursize, size);
-       if (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
+               Con_DPrintf("PRVM_SetTempString %s: cursize %i, new tempstring size %lu\n", prog->name, prog->tempstringsbuf.cursize, (unsigned long)size);
+       if ((size_t)prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
        {
                sizebuf_t old = prog->tempstringsbuf;
                if (prog->tempstringsbuf.cursize + size >= 1<<28)
-                       prog->error_cmd("PRVM_SetTempString: ran out of tempstring memory!  (refusing to grow tempstring buffer over 256MB, cursize %i, size %i)\n", prog->tempstringsbuf.cursize, size);
+                       prog->error_cmd("PRVM_SetTempString %s: ran out of tempstring memory!  (refusing to grow tempstring buffer over 256MB, cursize %i, new tempstring size %lu)\n", prog->name, prog->tempstringsbuf.cursize, (unsigned long)size);
                prog->tempstringsbuf.maxsize = max(prog->tempstringsbuf.maxsize, 65536);
-               while (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
+               while ((size_t)prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
                        prog->tempstringsbuf.maxsize *= 2;
                if (prog->tempstringsbuf.maxsize != old.maxsize || prog->tempstringsbuf.data == NULL)
                {
-                       Con_DPrintf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, prog->tempstringsbuf.maxsize/1024);
+                       Con_DPrintf("PRVM_SetTempString %s: enlarging tempstrings buffer (%iKB -> %iKB)\n", prog->name, old.maxsize/1024, prog->tempstringsbuf.maxsize/1024);
                        prog->tempstringsbuf.data = (unsigned char *) Mem_Alloc(prog->progs_mempool, prog->tempstringsbuf.maxsize);
                        if (old.data)
                        {
@@ -3408,16 +3429,16 @@ int PRVM_AllocString(prvm_prog_t *prog, size_t bufferlength, char **pointer)
 void PRVM_FreeString(prvm_prog_t *prog, int num)
 {
        if (num == 0)
-               prog->error_cmd("PRVM_FreeString: attempt to free a NULL string");
+               prog->error_cmd("PRVM_FreeString %s: attempt to free a NULL string", prog->name);
        else if (num >= 0 && num < prog->stringssize)
-               prog->error_cmd("PRVM_FreeString: attempt to free a constant string");
+               prog->error_cmd("PRVM_FreeString %s: attempt to free a constant string", prog->name);
        else if (num >= PRVM_KNOWNSTRINGBASE && num < PRVM_KNOWNSTRINGBASE + prog->numknownstrings)
        {
                num = num - PRVM_KNOWNSTRINGBASE;
                if (!prog->knownstrings[num])
-                       prog->error_cmd("PRVM_FreeString: attempt to free a non-existent or already freed string");
+                       prog->error_cmd("PRVM_FreeString %s: attempt to free a non-existent or already freed string", prog->name);
                if (!prog->knownstrings_flags[num])
-                       prog->error_cmd("PRVM_FreeString: attempt to free a string owned by the engine");
+                       prog->error_cmd("PRVM_FreeString %s: attempt to free a string owned by the engine", prog->name);
                PRVM_Free((char *)prog->knownstrings[num]);
                if(prog->leaktest_active)
                        if(prog->knownstrings_origin[num])
@@ -3427,7 +3448,7 @@ void PRVM_FreeString(prvm_prog_t *prog, int num)
                prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
        }
        else
-               prog->error_cmd("PRVM_FreeString: invalid string offset %i", num);
+               prog->error_cmd("PRVM_FreeString %s: invalid string offset %i", prog->name, num);
 }
 
 static qbool PRVM_IsStringReferenced(prvm_prog_t *prog, string_t string)
@@ -3683,7 +3704,7 @@ void PRVM_LeakTest(prvm_prog_t *prog)
 
 void PRVM_GarbageCollection(prvm_prog_t *prog)
 {
-       int limit = prvm_garbagecollection_scan_limit.integer;
+       int limit = prvm_garbagecollection_scan_limit.integer * (prog == SVVM_prog ? sv.frametime : cl.realframetime);
        prvm_prog_garbagecollection_state_t *gc = &prog->gc;
        if (!prvm_garbagecollection_enable.integer)
                return;
@@ -3802,5 +3823,6 @@ void PRVM_GarbageCollection(prvm_prog_t *prog)
        case PRVM_GC_RESET:
        default:
                memset(gc, 0, sizeof(*gc));
+//             Con_Printf("%s%s GC: reset @ %f frametime %f scan_limit per frame %i\n", prog == SVVM_prog ? "^6" : "^5", prog->name, host.realtime, prog == SVVM_prog ? sv.frametime : cl.realframetime, limit);
        }
 }
index fd0ff40b9f5fe802adea919a93cc0e288c68a419..d29aa7f559f7c68d44c382d53ce8eedbab0672c4 100644 (file)
@@ -431,17 +431,19 @@ void PRVM_StackTrace (prvm_prog_t *prog)
 
 void PRVM_ShortStackTrace(prvm_prog_t *prog, char *buf, size_t bufsize)
 {
-       mfunction_t     *f;
-       int                     i;
+       mfunction_t *f;
+       int i;
        char vabuf[1024];
+       char *p;
 
        if(prog)
        {
-               dpsnprintf(buf, bufsize, "(%s) ", prog->name);
+               i = dpsnprintf(buf, bufsize, "(%s) ", prog->name);
+               p = buf + max(0, i);
        }
        else
        {
-               strlcpy(buf, "<NO PROG>", bufsize);
+               dp_strlcpy(buf, "<NO PROG>", bufsize);
                return;
        }
 
@@ -450,13 +452,14 @@ void PRVM_ShortStackTrace(prvm_prog_t *prog, char *buf, size_t bufsize)
        for (i = prog->depth;i > 0;i--)
        {
                f = prog->stack[i].f;
-
-               if(strlcat(buf,
+               p = dp_stpecpy(
+                       p,
+                       buf + bufsize,
                        f
                                ? va(vabuf, sizeof(vabuf), "%s:%s(%i) ", PRVM_GetString(prog, f->s_file), PRVM_GetString(prog, f->s_name), prog->stack[i].s - f->first_statement)
-                               : "<NULL> ",
-                       bufsize
-               ) >= bufsize)
+                               : "<NULL> "
+                       );
+               if (p == buf + bufsize)
                        break;
        }
 }
@@ -712,26 +715,30 @@ void PRVM_PrintState(prvm_prog_t *prog, int stack_index)
 }
 
 extern cvar_t prvm_errordump;
-void PRVM_Crash(prvm_prog_t *prog)
+void PRVM_Crash(void)
 {
+       prvm_prog_t *prog;
        char vabuf[1024];
+       int i;
 
-       cl.csqc_loaded = false;
-
-       if (prog == NULL)
-               return;
-       if (!prog->loaded)
-               return;
+       // determine which program crashed
+       for (i = 0; i < PRVM_PROG_MAX; ++i)
+               if (PRVM_GetProg(i)->loaded && PRVM_GetProg(i)->depth > 0)
+                       break;
+       if (i >= PRVM_PROG_MAX)
+               return; // none of them crashed
+       prog = PRVM_GetProg(i);
 
-       PRVM_serverfunction(SV_Shutdown) = 0; // don't call SV_Shutdown on crash
+       Con_Printf("QuakeC crash report for %s:\n", prog->name);
+       PRVM_PrintState(prog, 0);
 
-       if( prog->depth > 0 )
-       {
-               Con_Printf("QuakeC crash report for %s:\n", prog->name);
-               PRVM_PrintState(prog, 0);
-       }
+       // don't call graceful shutdown on crash
+       if (prog == SVVM_prog)
+               PRVM_serverfunction(SV_Shutdown) = 0;
+       else if (prog == CLVM_prog)
+               PRVM_clientfunction(CSQC_Shutdown) = 0;
 
-       if(prvm_errordump.integer)
+       if(prvm_errordump.integer && (prog == SVVM_prog || prog == CLVM_prog))
        {
                // make a savegame
                SV_Savegame_to(prog, va(vabuf, sizeof(vabuf), "crash-%s.dmp", prog->name));
@@ -740,12 +747,6 @@ void PRVM_Crash(prvm_prog_t *prog)
        // dump the stack so host_error can shutdown functions
        prog->depth = 0;
        prog->localstack_used = 0;
-
-       // delete all tempstrings (FIXME: is this safe in VM->engine->VM recursion?)
-       prog->tempstringsbuf.cursize = 0;
-
-       // reset the prog pointer
-       prog = NULL;
 }
 
 /*
@@ -1023,7 +1024,7 @@ cleanup:
        if (prog == SVVM_prog)
                SV_FlushBroadcastMessages();
 }
-#endif
+#endif // CONFIG_MENU
 
 /*
 ====================
@@ -1132,7 +1133,7 @@ cleanup:
        if (prog == SVVM_prog)
                SV_FlushBroadcastMessages();
 }
-#endif
+#endif // PROFILING
 
 /*
 ====================
index d9331426cf6e11ee873bf8ab3f34af3f19d6270e..a386aa67b673528186be7ab828a7c6f44738caa3 100644 (file)
@@ -377,11 +377,8 @@ int i;
                                }
                                else
                                {
-                                       if (developer.integer)
-                                       {
-                                               PRE_ERROR();
-                                               VM_Warning(prog, "Attempted division by zero in %s\n", prog->name );
-                                       }
+                                       PRE_ERROR();
+                                       VM_Warning(prog, "Attempted division of %f by zero\n", OPA->_float);
                                        OPC->_float = 0.0f;
                                }
                                DISPATCH_OPCODE();
@@ -492,7 +489,7 @@ int i;
                                        if ((prvm_uint_t)OPB->_int < cached_entityfields && !cached_allowworldwrites)
                                        {
                                                PRE_ERROR();
-                                               VM_Warning(prog, "Attempted assignment to NULL entity field .%s (%i) in %s\n", PRVM_GetString(prog, PRVM_ED_FieldAtOfs(prog, OPB->_int)->s_name), (int)OPB->_int, prog->name);
+                                               VM_Warning(prog, "Attempted assignment to world.%s (edictnum 0 field %i) in %s\n", PRVM_GetString(prog, PRVM_ED_FieldAtOfs(prog, OPB->_int)->s_name), (int)OPB->_int, prog->name);
                                        }
                                }
                                ptr = (prvm_eval_t *)(cached_edictsfields + OPB->_int);
@@ -510,7 +507,7 @@ int i;
                                        if ((prvm_uint_t)OPB->_int < cached_entityfields && !cached_allowworldwrites)
                                        {
                                                PRE_ERROR();
-                                               VM_Warning(prog, "Attempted assignment to NULL entity field .%s (%i) in %s\n", PRVM_GetString(prog, PRVM_ED_FieldAtOfs(prog, OPB->_int)->s_name), (int)OPB->_int, prog->name);
+                                               VM_Warning(prog, "Attempted assignment to world.%s (edictnum 0 field %i) in %s\n", PRVM_GetString(prog, PRVM_ED_FieldAtOfs(prog, OPB->_int)->s_name), (int)OPB->_int, prog->name);
                                        }
                                }
                                // refresh the garbage collection on the string - this guards
@@ -534,7 +531,7 @@ int i;
                                        if ((prvm_uint_t)OPB->_int < cached_entityfields && !cached_allowworldwrites)
                                        {
                                                PRE_ERROR();
-                                               VM_Warning(prog, "Attempted assignment to NULL entity field .%s (%i) in %s\n", PRVM_GetString(prog, PRVM_ED_FieldAtOfs(prog, OPB->_int)->s_name), (int)OPB->_int, prog->name);
+                                               VM_Warning(prog, "Attempted assignment to world.%s (edictnum 0 field %i) in %s\n", PRVM_GetString(prog, PRVM_ED_FieldAtOfs(prog, OPB->_int)->s_name), (int)OPB->_int, prog->name);
                                        }
                                }
                                ptr = (prvm_eval_t *)(cached_edictsfields + OPB->_int);
@@ -560,7 +557,7 @@ int i;
                                if (OPA->edict == 0 && !cached_allowworldwrites)
                                {
                                        PRE_ERROR();
-                                       prog->error_cmd("Forbidden assignment to NULL entity in %s", prog->name);
+                                       prog->error_cmd("Forbidden assignment to world (edictnum 0) in %s", prog->name);
                                        goto cleanup;
                                }
 #endif
index 97a2defbcc3d5a0a5fbcd3caef342d97545f9cbd..aea8da3e9f0720a1396d4cf1761888860e186eb2 100644 (file)
@@ -94,6 +94,8 @@ PRVM_DECLARE_clientfunction(CSQC_Parse_StuffCmd)
 PRVM_DECLARE_clientfunction(CSQC_Parse_TempEntity)
 PRVM_DECLARE_clientfunction(CSQC_Shutdown)
 PRVM_DECLARE_clientfunction(CSQC_UpdateView)
+PRVM_DECLARE_clientfunction(CSQC_DrawHud)
+PRVM_DECLARE_clientfunction(CSQC_DrawScores)
 PRVM_DECLARE_clientfunction(GameCommand)
 PRVM_DECLARE_clientfunction(URI_Get_Callback)
 PRVM_DECLARE_clientglobaledict(other)
@@ -416,6 +418,8 @@ PRVM_DECLARE_function(CSQC_Parse_StuffCmd)
 PRVM_DECLARE_function(CSQC_Parse_TempEntity)
 PRVM_DECLARE_function(CSQC_Shutdown)
 PRVM_DECLARE_function(CSQC_UpdateView)
+PRVM_DECLARE_function(CSQC_DrawHud)
+PRVM_DECLARE_function(CSQC_DrawScores)
 PRVM_DECLARE_function(ClientConnect)
 PRVM_DECLARE_function(ClientDisconnect)
 PRVM_DECLARE_function(ClientKill)
diff --git a/qdefs.h b/qdefs.h
index 9371f06eecc703be1efc0afb15305e2ffcf729f8..a79230db806274eecef1399a97b923f3ee5f0e78 100644 (file)
--- a/qdefs.h
+++ b/qdefs.h
@@ -91,7 +91,7 @@
 #define SERVERLIST_ANDMASKCOUNT                5
 #define SERVERLIST_ORMASKCOUNT         5
 #else
-#define        MAX_INPUTLINE                   16384 ///< maximum length of console commandline, QuakeC strings, and many other text processing buffers
+#define        MAX_INPUTLINE                   16384 ///< maximum size of console commandline, QuakeC strings, and many other text processing buffers
 #define        CON_TEXTSIZE                    1048576 ///< max scrollback buffer characters in console
 #define        CON_MAXLINES                    16384 ///< max scrollback buffer lines in console
 #define        HIST_TEXTSIZE                   262144 ///< max command history buffer characters in console
index ec5c72b6efb29fbb305e5d0f0ed17bb542fd3a24..510b4ec20bc7b4d90ae6228d96ffcb42c5814a11 100644 (file)
@@ -35,14 +35,13 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #define MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE 0x00000004
 
 // 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_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
@@ -55,14 +54,15 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #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)
+#define IT_INVISIBILITY         524288
+#define IT_INVULNERABILITY      1048576
+#define IT_SUIT                 2097152
+#define IT_QUAD                 4194304
+#define IT_SIGIL1               (1u<<28)
+#define IT_SIGIL2               (1u<<29)
+#define IT_SIGIL3               (1u<<30)
+#define IT_SIGIL4               (1u<<31)
+// UBSan: unsigned literals because left shifting by 31 causes signed overflow, although it works as expected on x86.
 
 //===========================================
 // AK nexuiz changed and added defines
@@ -140,6 +140,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include "progs.h"
 #include "progsvm.h"
 #include "server.h"
+#include "phys.h"
 
 #include "input.h"
 #include "keys.h"
index 904977146ece0e69771e7ebe2cb1a1838c4b5650..dd76b6cae9d4be1fcf74b17d74e09f2e85a0838e 100644 (file)
@@ -55,9 +55,10 @@ void R_Modules_Start(void)
                        continue;
                if (rendermodule[i].active)
                {
-                       Con_Printf ("R_StartModules: module \"%s\" already active\n", rendermodule[i].name);
+                       Con_Printf ("R_Modules_Start: module \"%s\" already active\n", rendermodule[i].name);
                        continue;
                }
+               Con_DPrintf("Starting render module \"%s\"\n", rendermodule[i].name);
                rendermodule[i].active = 1;
                rendermodule[i].start();
        }
@@ -73,6 +74,7 @@ void R_Modules_Shutdown(void)
                        continue;
                if (!rendermodule[i].active)
                        continue;
+               Con_DPrintf("Stopping render module \"%s\"\n", rendermodule[i].name);
                rendermodule[i].active = 0;
                rendermodule[i].shutdown();
        }
@@ -81,7 +83,8 @@ void R_Modules_Shutdown(void)
 void R_Modules_Restart_f(cmd_state_t *cmd)
 {
        CL_StartVideo();
-       Con_Print("restarting renderer\n");
+       Con_Print("Restarting renderer\n");
+       SCR_BeginLoadingPlaque(false);
        R_Modules_Shutdown();
        R_Modules_Start();
 }
index 7c23361bc73c8af913057b5a886c39d9c0b2aa33..2c5cecd34b75bbf65a2017dce825bf46ede3f39b 100644 (file)
@@ -232,7 +232,7 @@ cvar_t r_shadow_bouncegrid_subsamples = {CF_CLIENT | CF_ARCHIVE, "r_shadow_bounc
 cvar_t r_shadow_bouncegrid_threaded = {CF_CLIENT | CF_ARCHIVE, "r_shadow_bouncegrid_threaded", "1", "enables use of taskqueue_maxthreads to perform the traces and slice rendering of bouncegrid"};
 cvar_t r_coronas = {CF_CLIENT | CF_ARCHIVE, "r_coronas", "0", "brightness of corona flare effects around certain lights, 0 disables corona effects"};
 cvar_t r_coronas_occlusionsizescale = {CF_CLIENT | CF_ARCHIVE, "r_coronas_occlusionsizescale", "0.1", "size of light source for corona occlusion checksum the proportion of hidden pixels controls corona intensity"};
-cvar_t r_coronas_occlusionquery = {CF_CLIENT | CF_ARCHIVE, "r_coronas_occlusionquery", "0", "fades coronas according to visibility"};
+cvar_t r_coronas_occlusionquery = {CF_CLIENT | CF_ARCHIVE, "r_coronas_occlusionquery", "0", "fades coronas according to visibility, requires OpenGL 4.4"};
 cvar_t gl_flashblend = {CF_CLIENT | CF_ARCHIVE, "gl_flashblend", "0", "render bright coronas for dynamic lights instead of actual lighting, fast but ugly"};
 cvar_t r_editlights = {CF_CLIENT, "r_editlights", "0", "enables .rtlights file editing mode"};
 cvar_t r_editlights_cursordistance = {CF_CLIENT, "r_editlights_cursordistance", "1024", "maximum distance of cursor from eye"};
@@ -487,6 +487,18 @@ static void r_shadow_start(void)
        }
 }
 
+static void R_Shadow_BounceGrid_FreeHighPixels(void)
+{
+       r_shadow_bouncegrid_state.highpixels = NULL;
+       if (r_shadow_bouncegrid_state.blurpixels[0]) { Mem_Free(r_shadow_bouncegrid_state.blurpixels[0]); r_shadow_bouncegrid_state.blurpixels[0] = NULL; }
+       if (r_shadow_bouncegrid_state.blurpixels[1]) { Mem_Free(r_shadow_bouncegrid_state.blurpixels[1]); r_shadow_bouncegrid_state.blurpixels[1] = NULL; }
+       if (r_shadow_bouncegrid_state.u8pixels)      { Mem_Free(r_shadow_bouncegrid_state.u8pixels);      r_shadow_bouncegrid_state.u8pixels      = NULL; }
+       if (r_shadow_bouncegrid_state.fp16pixels)    { Mem_Free(r_shadow_bouncegrid_state.fp16pixels);    r_shadow_bouncegrid_state.fp16pixels    = NULL; }
+       if (r_shadow_bouncegrid_state.photons)       { Mem_Free(r_shadow_bouncegrid_state.photons);       r_shadow_bouncegrid_state.photons       = NULL; }
+       if (r_shadow_bouncegrid_state.photons_tasks) { Mem_Free(r_shadow_bouncegrid_state.photons_tasks); r_shadow_bouncegrid_state.photons_tasks = NULL; }
+       if (r_shadow_bouncegrid_state.slices_tasks)  { Mem_Free(r_shadow_bouncegrid_state.slices_tasks);  r_shadow_bouncegrid_state.slices_tasks  = NULL; }
+}
+
 static void R_Shadow_FreeDeferred(void);
 static void r_shadow_shutdown(void)
 {
@@ -505,14 +517,7 @@ static void r_shadow_shutdown(void)
        if (r_shadow_scenelightlist)
                Mem_Free(r_shadow_scenelightlist);
        r_shadow_scenelightlist = NULL;
-       r_shadow_bouncegrid_state.highpixels = NULL;
-       if (r_shadow_bouncegrid_state.blurpixels[0]) Mem_Free(r_shadow_bouncegrid_state.blurpixels[0]); r_shadow_bouncegrid_state.blurpixels[0] = NULL;
-       if (r_shadow_bouncegrid_state.blurpixels[1]) Mem_Free(r_shadow_bouncegrid_state.blurpixels[1]); r_shadow_bouncegrid_state.blurpixels[1] = NULL;
-       if (r_shadow_bouncegrid_state.u8pixels) Mem_Free(r_shadow_bouncegrid_state.u8pixels); r_shadow_bouncegrid_state.u8pixels = NULL;
-       if (r_shadow_bouncegrid_state.fp16pixels) Mem_Free(r_shadow_bouncegrid_state.fp16pixels); r_shadow_bouncegrid_state.fp16pixels = NULL;
-       if (r_shadow_bouncegrid_state.photons) Mem_Free(r_shadow_bouncegrid_state.photons); r_shadow_bouncegrid_state.photons = NULL;
-       if (r_shadow_bouncegrid_state.photons_tasks) Mem_Free(r_shadow_bouncegrid_state.photons_tasks); r_shadow_bouncegrid_state.photons_tasks = NULL;
-       if (r_shadow_bouncegrid_state.slices_tasks) Mem_Free(r_shadow_bouncegrid_state.slices_tasks); r_shadow_bouncegrid_state.slices_tasks = NULL;
+       R_Shadow_BounceGrid_FreeHighPixels();
        memset(&r_shadow_bouncegrid_state, 0, sizeof(r_shadow_bouncegrid_state));
        r_shadow_attenuationgradienttexture = NULL;
        R_FreeTexturePool(&r_shadow_texturepool);
@@ -579,14 +584,7 @@ static void r_shadow_shutdown(void)
 
 static void r_shadow_newmap(void)
 {
-       r_shadow_bouncegrid_state.highpixels = NULL;
-       if (r_shadow_bouncegrid_state.blurpixels[0]) { Mem_Free(r_shadow_bouncegrid_state.blurpixels[0]); r_shadow_bouncegrid_state.blurpixels[0] = NULL; }
-       if (r_shadow_bouncegrid_state.blurpixels[1]) { Mem_Free(r_shadow_bouncegrid_state.blurpixels[1]); r_shadow_bouncegrid_state.blurpixels[1] = NULL; }
-       if (r_shadow_bouncegrid_state.u8pixels) { Mem_Free(r_shadow_bouncegrid_state.u8pixels); r_shadow_bouncegrid_state.u8pixels = NULL; }
-       if (r_shadow_bouncegrid_state.fp16pixels) { Mem_Free(r_shadow_bouncegrid_state.fp16pixels); r_shadow_bouncegrid_state.fp16pixels = NULL; }
-       if (r_shadow_bouncegrid_state.photons) Mem_Free(r_shadow_bouncegrid_state.photons); r_shadow_bouncegrid_state.photons = NULL;
-       if (r_shadow_bouncegrid_state.photons_tasks) Mem_Free(r_shadow_bouncegrid_state.photons_tasks); r_shadow_bouncegrid_state.photons_tasks = NULL;
-       if (r_shadow_bouncegrid_state.slices_tasks) Mem_Free(r_shadow_bouncegrid_state.slices_tasks); r_shadow_bouncegrid_state.slices_tasks = NULL;
+       R_Shadow_BounceGrid_FreeHighPixels();
 
        if (r_shadow_bouncegrid_state.texture)    { R_FreeTexture(r_shadow_bouncegrid_state.texture);r_shadow_bouncegrid_state.texture = NULL; }
        if (r_shadow_lightcorona)                 { R_SkinFrame_MarkUsed(r_shadow_lightcorona); }
@@ -1228,7 +1226,7 @@ static unsigned int R_Shadow_MakeTextures_SamplePoint(float x, float y, float z)
        float dist = sqrt(x*x+y*y+z*z);
        float intensity = dist < 1 ? ((1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist)) : 0;
        // note this code could suffer byte order issues except that it is multiplying by an integer that reads the same both ways
-       return (unsigned char)bound(0, intensity * 256.0f, 255) * 0x01010101;
+       return bound(0, (unsigned int)(intensity * 256.0f), 255) * 0x01010101U;
 }
 
 static void R_Shadow_MakeTextures(void)
@@ -1884,15 +1882,7 @@ static void R_Shadow_BounceGrid_UpdateSpacing(void)
        {
                if (r_shadow_bouncegrid_state.texture) { R_FreeTexture(r_shadow_bouncegrid_state.texture);r_shadow_bouncegrid_state.texture = NULL; }
 
-               r_shadow_bouncegrid_state.highpixels = NULL;
-
-               if (r_shadow_bouncegrid_state.blurpixels[0]) { Mem_Free(r_shadow_bouncegrid_state.blurpixels[0]); r_shadow_bouncegrid_state.blurpixels[0] = NULL; }
-               if (r_shadow_bouncegrid_state.blurpixels[1]) { Mem_Free(r_shadow_bouncegrid_state.blurpixels[1]); r_shadow_bouncegrid_state.blurpixels[1] = NULL; }
-               if (r_shadow_bouncegrid_state.u8pixels) { Mem_Free(r_shadow_bouncegrid_state.u8pixels); r_shadow_bouncegrid_state.u8pixels = NULL; }
-               if (r_shadow_bouncegrid_state.fp16pixels) { Mem_Free(r_shadow_bouncegrid_state.fp16pixels); r_shadow_bouncegrid_state.fp16pixels = NULL; }
-               if (r_shadow_bouncegrid_state.photons) { Mem_Free(r_shadow_bouncegrid_state.photons); r_shadow_bouncegrid_state.photons = NULL; }
-               if (r_shadow_bouncegrid_state.photons_tasks) { Mem_Free(r_shadow_bouncegrid_state.photons_tasks); r_shadow_bouncegrid_state.photons_tasks = NULL; }
-               if (r_shadow_bouncegrid_state.slices_tasks) Mem_Free(r_shadow_bouncegrid_state.slices_tasks); r_shadow_bouncegrid_state.slices_tasks = NULL;
+               R_Shadow_BounceGrid_FreeHighPixels();
 
                r_shadow_bouncegrid_state.numpixels = numpixels;
        }
@@ -2787,14 +2777,7 @@ void R_Shadow_UpdateBounceGridTexture(void)
                        R_FreeTexture(r_shadow_bouncegrid_state.texture);
                        r_shadow_bouncegrid_state.texture = NULL;
                }
-               r_shadow_bouncegrid_state.highpixels = NULL;
-               if (r_shadow_bouncegrid_state.blurpixels[0]) Mem_Free(r_shadow_bouncegrid_state.blurpixels[0]); r_shadow_bouncegrid_state.blurpixels[0] = NULL;
-               if (r_shadow_bouncegrid_state.blurpixels[1]) Mem_Free(r_shadow_bouncegrid_state.blurpixels[1]); r_shadow_bouncegrid_state.blurpixels[1] = NULL;
-               if (r_shadow_bouncegrid_state.u8pixels) Mem_Free(r_shadow_bouncegrid_state.u8pixels); r_shadow_bouncegrid_state.u8pixels = NULL;
-               if (r_shadow_bouncegrid_state.fp16pixels) Mem_Free(r_shadow_bouncegrid_state.fp16pixels); r_shadow_bouncegrid_state.fp16pixels = NULL;
-               if (r_shadow_bouncegrid_state.photons) Mem_Free(r_shadow_bouncegrid_state.photons); r_shadow_bouncegrid_state.photons = NULL;
-               if (r_shadow_bouncegrid_state.photons_tasks) Mem_Free(r_shadow_bouncegrid_state.photons_tasks); r_shadow_bouncegrid_state.photons_tasks = NULL;
-               if (r_shadow_bouncegrid_state.slices_tasks) Mem_Free(r_shadow_bouncegrid_state.slices_tasks); r_shadow_bouncegrid_state.slices_tasks = NULL;
+               R_Shadow_BounceGrid_FreeHighPixels();
                r_shadow_bouncegrid_state.numpixels = 0;
                r_shadow_bouncegrid_state.numphotons = 0;
                r_shadow_bouncegrid_state.directional = false;
@@ -2871,16 +2854,7 @@ void R_Shadow_UpdateBounceGridTexture(void)
 
        // after we compute the static lighting we don't need to keep the highpixels array around
        if (settings.staticmode)
-       {
-               r_shadow_bouncegrid_state.highpixels = NULL;
-               if (r_shadow_bouncegrid_state.blurpixels[0]) Mem_Free(r_shadow_bouncegrid_state.blurpixels[0]); r_shadow_bouncegrid_state.blurpixels[0] = NULL;
-               if (r_shadow_bouncegrid_state.blurpixels[1]) Mem_Free(r_shadow_bouncegrid_state.blurpixels[1]); r_shadow_bouncegrid_state.blurpixels[1] = NULL;
-               if (r_shadow_bouncegrid_state.u8pixels) Mem_Free(r_shadow_bouncegrid_state.u8pixels); r_shadow_bouncegrid_state.u8pixels = NULL;
-               if (r_shadow_bouncegrid_state.fp16pixels) Mem_Free(r_shadow_bouncegrid_state.fp16pixels); r_shadow_bouncegrid_state.fp16pixels = NULL;
-               if (r_shadow_bouncegrid_state.photons) Mem_Free(r_shadow_bouncegrid_state.photons); r_shadow_bouncegrid_state.photons = NULL;
-               if (r_shadow_bouncegrid_state.photons_tasks) Mem_Free(r_shadow_bouncegrid_state.photons_tasks); r_shadow_bouncegrid_state.photons_tasks = NULL;
-               if (r_shadow_bouncegrid_state.slices_tasks) Mem_Free(r_shadow_bouncegrid_state.slices_tasks); r_shadow_bouncegrid_state.slices_tasks = NULL;
-       }
+               R_Shadow_BounceGrid_FreeHighPixels();
 }
 
 void R_Shadow_RenderMode_VisibleLighting(qbool transparent)
@@ -2985,7 +2959,7 @@ void R_Shadow_RenderLighting(int texturenumsurfaces, const msurface_t **textures
                VectorNegate(specularcolor, specularcolor);
                GL_BlendEquationSubtract(true);
        }
-       RSurf_SetupDepthAndCulling();
+       RSurf_SetupDepthAndCulling(false);
        switch (r_shadow_rendermode)
        {
        case R_SHADOW_RENDERMODE_VISIBLELIGHTING:
@@ -3022,7 +2996,7 @@ void R_RTLight_Update(rtlight_t *rtlight, int isstatic, matrix4x4_t *matrix, vec
        VectorCopy(color, rtlight->color);
        rtlight->cubemapname[0] = 0;
        if (cubemapname && cubemapname[0])
-               strlcpy(rtlight->cubemapname, cubemapname, sizeof(rtlight->cubemapname));
+               dp_strlcpy(rtlight->cubemapname, cubemapname, sizeof(rtlight->cubemapname));
        rtlight->shadow = shadow;
        rtlight->corona = corona;
        rtlight->style = style;
@@ -4587,7 +4561,8 @@ void R_Shadow_DrawCoronas(void)
        {
        case RENDERPATH_GL32:
        case RENDERPATH_GLES2:
-               usequery = r_coronas_occlusionquery.integer;
+               // buffer binding target GL_QUERY_BUFFER: Core since version 4.4
+               usequery = r_coronas_occlusionquery.integer && vid.support.glversion >= 44;
 #ifndef USE_GLES2
                if (usequery)
                {
@@ -4701,7 +4676,7 @@ static void R_Shadow_UpdateWorldLight(dlight_t *light, vec3_t origin, vec3_t ang
        light->style = style;
        light->shadow = shadowenable;
        light->corona = corona;
-       strlcpy(light->cubemapname, cubemapname, sizeof(light->cubemapname));
+       dp_strlcpy(light->cubemapname, cubemapname, sizeof(light->cubemapname));
        light->coronasizescale = coronasizescale;
        light->ambientscale = ambientscale;
        light->diffusescale = diffusescale;
@@ -5125,14 +5100,14 @@ void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
                        if (com_token[0] == '}')
                                break; // end of entity
                        if (com_token[0] == '_')
-                               strlcpy(key, com_token + 1, sizeof(key));
+                               dp_strlcpy(key, com_token + 1, sizeof(key));
                        else
-                               strlcpy(key, com_token, sizeof(key));
+                               dp_strlcpy(key, com_token, sizeof(key));
                        while (key[strlen(key)-1] == ' ') // remove trailing spaces
                                key[strlen(key)-1] = 0;
                        if (!COM_ParseToken_Simple(&data, false, false, true))
                                break; // error
-                       strlcpy(value, com_token, sizeof(value));
+                       dp_strlcpy(value, com_token, sizeof(value));
 
                        // now that we have the key pair worked out...
                        if (!strcmp("light", key))
@@ -5344,7 +5319,7 @@ void R_Shadow_EditLights_Reload_f(cmd_state_t *cmd)
 {
        if (!cl.worldmodel)
                return;
-       strlcpy(r_shadow_mapname, cl.worldname, sizeof(r_shadow_mapname));
+       dp_strlcpy(r_shadow_mapname, cl.worldname, sizeof(r_shadow_mapname));
        R_Shadow_ClearWorldLights();
        if (r_shadow_realtime_world_importlightentitiesfrommap.integer <= 1)
        {
@@ -5417,7 +5392,7 @@ static void R_Shadow_EditLights_Edit_f(cmd_state_t *cmd)
        radius = r_shadow_selectedlight->radius;
        style = r_shadow_selectedlight->style;
        if (*r_shadow_selectedlight->cubemapname)
-               strlcpy(cubemapname, r_shadow_selectedlight->cubemapname, sizeof(cubemapname));
+               dp_strlcpy(cubemapname, r_shadow_selectedlight->cubemapname, sizeof(cubemapname));
        else
                cubemapname[0] = 0;
        shadows = r_shadow_selectedlight->shadow;
@@ -5621,7 +5596,7 @@ static void R_Shadow_EditLights_Edit_f(cmd_state_t *cmd)
                        return;
                }
                if (Cmd_Argc(cmd) == 3)
-                       strlcpy(cubemapname, Cmd_Argv(cmd, 2), sizeof(cubemapname));
+                       dp_strlcpy(cubemapname, Cmd_Argv(cmd, 2), sizeof(cubemapname));
                else
                        cubemapname[0] = 0;
        }
@@ -5952,7 +5927,7 @@ static void R_Shadow_EditLights_CopyInfo_f(cmd_state_t *cmd)
        r_shadow_bufferlight.radius = r_shadow_selectedlight->radius;
        r_shadow_bufferlight.style = r_shadow_selectedlight->style;
        if (*r_shadow_selectedlight->cubemapname)
-               strlcpy(r_shadow_bufferlight.cubemapname, r_shadow_selectedlight->cubemapname, sizeof(r_shadow_bufferlight.cubemapname));
+               dp_strlcpy(r_shadow_bufferlight.cubemapname, r_shadow_selectedlight->cubemapname, sizeof(r_shadow_bufferlight.cubemapname));
        else
                r_shadow_bufferlight.cubemapname[0] = 0;
        r_shadow_bufferlight.shadow = r_shadow_selectedlight->shadow;
diff --git a/r_sky.c b/r_sky.c
index 7fd1463dbc7239c60c998ea3124db9ca861234a5..67ece7289ce60c8d8bc4e89f31c0af1114216bce 100644 (file)
--- a/r_sky.c
+++ b/r_sky.c
@@ -157,7 +157,7 @@ int R_SetSkyBox(const char *sky)
                return false;
        }
 
-       strlcpy(skyname, sky, sizeof(skyname));
+       dp_strlcpy(skyname, sky, sizeof(skyname));
 
        return R_LoadSkyBox();
 }
@@ -432,7 +432,7 @@ void R_Sky(void)
                //GL_Clear(GL_DEPTH_BUFFER_BIT);
        }
        */
-       GL_Scissor(0, 0, r_fb.screentexturewidth, r_fb.screentextureheight);
+       GL_Scissor(r_refdef.view.viewport.x, r_refdef.view.viewport.y, r_refdef.view.viewport.width, r_refdef.view.viewport.height);
 }
 
 //===============================================================
index 6dfc7938f917c294aa551e31af936c77f44e1e26..55c43e7ed8a7272a689baa23a463c6c434091e98 100644 (file)
--- a/r_stats.c
+++ b/r_stats.c
@@ -218,10 +218,10 @@ void R_TimeReport(const char *desc)
 
        if (speedstringcount + length > (vid_conwidth.integer / 8))
        {
-               strlcat(r_speeds_timestring, "\n", sizeof(r_speeds_timestring));
+               dp_strlcat(r_speeds_timestring, "\n", sizeof(r_speeds_timestring));
                speedstringcount = 0;
        }
-       strlcat(r_speeds_timestring, tempbuf, sizeof(r_speeds_timestring));
+       dp_strlcat(r_speeds_timestring, tempbuf, sizeof(r_speeds_timestring));
        speedstringcount += length;
 }
 
index e7b99a665ff59bed1deb7394c2f8659bf9ec313d..fe21b6000a1e266cb987ae4dc3ddaf7c440664e2 100644 (file)
--- a/render.h
+++ b/render.h
@@ -46,7 +46,7 @@ typedef enum shaderlanguage_e
 }
 shaderlanguage_t;
 
-// this enum selects which of the glslshadermodeinfo entries should be used
+/// this enum selects which of the glslshadermodeinfo entries should be used
 typedef enum shadermode_e
 {
        SHADERMODE_GENERIC, ///< (particles/HUD/etc) vertex color, optionally multiplied by one texture
@@ -70,39 +70,40 @@ typedef enum shadermode_e
 }
 shadermode_t;
 
-#define SHADERPERMUTATION_DIFFUSE (1<<0) ///< (lightsource) whether to use directional shading
-#define SHADERPERMUTATION_VERTEXTEXTUREBLEND (1<<1) ///< indicates this is a two-layer material blend based on vertex alpha (q3bsp)
-#define        SHADERPERMUTATION_VIEWTINT (1<<2) ///< view tint (postprocessing only), use vertex colors (generic only)
-#define        SHADERPERMUTATION_COLORMAPPING (1<<3) ///< indicates this is a colormapped skin
-#define        SHADERPERMUTATION_SATURATION (1<<4) ///< saturation (postprocessing only)
-#define        SHADERPERMUTATION_FOGINSIDE (1<<5) ///< tint the color by fog color or black if using additive blend mode
-#define        SHADERPERMUTATION_FOGOUTSIDE (1<<6) ///< tint the color by fog color or black if using additive blend mode
-#define        SHADERPERMUTATION_FOGHEIGHTTEXTURE (1<<7) ///< fog color and density determined by texture mapped on vertical axis
-#define        SHADERPERMUTATION_FOGALPHAHACK (1<<8) ///< fog color and density determined by texture mapped on vertical axis
-#define        SHADERPERMUTATION_GAMMARAMPS (1<<9) ///< gamma (postprocessing only)
-#define        SHADERPERMUTATION_CUBEFILTER (1<<10) ///< (lightsource) use cubemap light filter
-#define        SHADERPERMUTATION_GLOW (1<<11) ///< (lightmap) blend in an additive glow texture
-#define        SHADERPERMUTATION_BLOOM (1<<12) ///< bloom (postprocessing only)
-#define        SHADERPERMUTATION_SPECULAR (1<<13) ///< (lightsource or deluxemapping) render specular effects
-#define        SHADERPERMUTATION_POSTPROCESSING (1<<14) ///< user defined postprocessing (postprocessing only)
-#define        SHADERPERMUTATION_REFLECTION (1<<15) ///< normalmap-perturbed reflection of the scene infront of the surface, preformed as an overlay on the surface
-#define        SHADERPERMUTATION_OFFSETMAPPING (1<<16) ///< adjust texcoords to roughly simulate a displacement mapped surface
-#define        SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING (1<<17) ///< adjust texcoords to accurately simulate a displacement mapped surface (requires OFFSETMAPPING to also be set!)
-#define        SHADERPERMUTATION_SHADOWMAP2D (1<<18) ///< (lightsource) use shadowmap texture as light filter
-#define        SHADERPERMUTATION_SHADOWMAPVSDCT (1<<19) ///< (lightsource) use virtual shadow depth cube texture for shadowmap indexing
-#define        SHADERPERMUTATION_SHADOWMAPORTHO (1<<20) ///< (lightsource) use orthographic shadowmap projection
-#define        SHADERPERMUTATION_DEFERREDLIGHTMAP (1<<21) ///< (lightmap) read Texture_ScreenDiffuse/Specular textures and add them on top of lightmapping
-#define        SHADERPERMUTATION_ALPHAKILL (1<<22) ///< (deferredgeometry) discard pixel if diffuse texture alpha below 0.5, (generic) apply global alpha
-#define        SHADERPERMUTATION_REFLECTCUBE (1<<23) ///< fake reflections using global cubemap (not HDRI light probe)
-#define        SHADERPERMUTATION_NORMALMAPSCROLLBLEND (1<<24) ///< (water) counter-direction normalmaps scrolling
-#define        SHADERPERMUTATION_BOUNCEGRID (1<<25) ///< (lightmap) use Texture_BounceGrid as an additional source of ambient light
-#define        SHADERPERMUTATION_BOUNCEGRIDDIRECTIONAL (1<<26) ///< (lightmap) use 16-component pixels in bouncegrid texture for directional lighting rather than standard 4-component
-#define SHADERPERMUTATION_TRIPPY (1<<27) ///< use trippy vertex shader effect
-#define        SHADERPERMUTATION_DEPTHRGB (1<<28) ///< read/write depth values in RGB color coded format for older hardware without depth samplers
-#define        SHADERPERMUTATION_ALPHAGEN_VERTEX (1<<29) ///< alphaGen vertex
-#define        SHADERPERMUTATION_SKELETAL (1<<30) ///< (skeletal models) use skeletal matrices to deform vertices (gpu-skinning)
-#define        SHADERPERMUTATION_OCCLUDE (1<<31) ///< use occlusion buffer for corona
-#define        SHADERPERMUTATION_COUNT 32 ///< size of shaderpermutationinfo array
+#define SHADERPERMUTATION_DIFFUSE (1u<<0) ///< (lightsource) whether to use directional shading
+#define SHADERPERMUTATION_VERTEXTEXTUREBLEND (1u<<1) ///< indicates this is a two-layer material blend based on vertex alpha (q3bsp)
+#define SHADERPERMUTATION_VIEWTINT (1u<<2) ///< view tint (postprocessing only), use vertex colors (generic only)
+#define SHADERPERMUTATION_COLORMAPPING (1u<<3) ///< indicates this is a colormapped skin
+#define SHADERPERMUTATION_SATURATION (1u<<4) ///< saturation (postprocessing only)
+#define SHADERPERMUTATION_FOGINSIDE (1u<<5) ///< tint the color by fog color or black if using additive blend mode
+#define SHADERPERMUTATION_FOGOUTSIDE (1u<<6) ///< tint the color by fog color or black if using additive blend mode
+#define SHADERPERMUTATION_FOGHEIGHTTEXTURE (1u<<7) ///< fog color and density determined by texture mapped on vertical axis
+#define SHADERPERMUTATION_FOGALPHAHACK (1u<<8) ///< fog color and density determined by texture mapped on vertical axis
+#define SHADERPERMUTATION_GAMMARAMPS (1u<<9) ///< gamma (postprocessing only)
+#define SHADERPERMUTATION_CUBEFILTER (1u<<10) ///< (lightsource) use cubemap light filter
+#define SHADERPERMUTATION_GLOW (1u<<11) ///< (lightmap) blend in an additive glow texture
+#define SHADERPERMUTATION_BLOOM (1u<<12) ///< bloom (postprocessing only)
+#define SHADERPERMUTATION_SPECULAR (1u<<13) ///< (lightsource or deluxemapping) render specular effects
+#define SHADERPERMUTATION_POSTPROCESSING (1u<<14) ///< user defined postprocessing (postprocessing only)
+#define SHADERPERMUTATION_REFLECTION (1u<<15) ///< normalmap-perturbed reflection of the scene infront of the surface, preformed as an overlay on the surface
+#define SHADERPERMUTATION_OFFSETMAPPING (1u<<16) ///< adjust texcoords to roughly simulate a displacement mapped surface
+#define SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING (1u<<17) ///< adjust texcoords to accurately simulate a displacement mapped surface (requires OFFSETMAPPING to also be set!)
+#define SHADERPERMUTATION_SHADOWMAP2D (1u<<18) ///< (lightsource) use shadowmap texture as light filter
+#define SHADERPERMUTATION_SHADOWMAPVSDCT (1u<<19) ///< (lightsource) use virtual shadow depth cube texture for shadowmap indexing
+#define SHADERPERMUTATION_SHADOWMAPORTHO (1u<<20) ///< (lightsource) use orthographic shadowmap projection
+#define SHADERPERMUTATION_DEFERREDLIGHTMAP (1u<<21) ///< (lightmap) read Texture_ScreenDiffuse/Specular textures and add them on top of lightmapping
+#define SHADERPERMUTATION_ALPHAKILL (1u<<22) ///< (deferredgeometry) discard pixel if diffuse texture alpha below 0.5, (generic) apply global alpha
+#define SHADERPERMUTATION_REFLECTCUBE (1u<<23) ///< fake reflections using global cubemap (not HDRI light probe)
+#define SHADERPERMUTATION_NORMALMAPSCROLLBLEND (1u<<24) ///< (water) counter-direction normalmaps scrolling
+#define SHADERPERMUTATION_BOUNCEGRID (1u<<25) ///< (lightmap) use Texture_BounceGrid as an additional source of ambient light
+#define SHADERPERMUTATION_BOUNCEGRIDDIRECTIONAL (1u<<26) ///< (lightmap) use 16-component pixels in bouncegrid texture for directional lighting rather than standard 4-component
+#define SHADERPERMUTATION_TRIPPY (1u<<27) ///< use trippy vertex shader effect
+#define SHADERPERMUTATION_DEPTHRGB (1u<<28) ///< read/write depth values in RGB color coded format for older hardware without depth samplers
+#define SHADERPERMUTATION_ALPHAGEN_VERTEX (1u<<29) ///< alphaGen vertex
+#define SHADERPERMUTATION_SKELETAL (1u<<30) ///< (skeletal models) use skeletal matrices to deform vertices (gpu-skinning)
+#define SHADERPERMUTATION_OCCLUDE (1u<<31) ///< use occlusion buffer for corona
+#define SHADERPERMUTATION_COUNT 32u ///< size of shaderpermutationinfo array
+// UBSan: unsigned literals because left shifting by 31 causes signed overflow, although it works as expected on x86.
 
 // 1.0f / N table
 extern float ixtable[4096];
@@ -215,8 +216,8 @@ void R_UpdateVariables(void); // must call after setting up most of r_refdef, bu
 void R_RenderView(int fbo, rtexture_t *depthtexture, rtexture_t *colortexture, int x, int y, int width, int height); // must set r_refdef and call R_UpdateVariables and CL_UpdateEntityShading first
 void R_RenderView_UpdateViewVectors(void); // just updates r_refdef.view.{forward,left,up,origin,right,inverse_matrix}
 
-float RSurf_FogVertex(const vec3_t p);
-float RSurf_FogPoint(const vec3_t p);
+float RSurf_FogPoint(const float *v);
+float RSurf_FogVertex(const float *v);
 
 typedef enum r_refdef_scene_type_s {
        RST_CLIENT,
@@ -236,9 +237,9 @@ r_viewport_type_t;
 
 typedef struct r_viewport_s
 {
-       matrix4x4_t cameramatrix; // from entity (transforms from camera entity to world)
-       matrix4x4_t viewmatrix; // actual matrix for rendering (transforms to viewspace)
-       matrix4x4_t projectmatrix; // actual projection matrix (transforms from viewspace to screen)
+       matrix4x4_t cameramatrix;  ///< from entity (transforms from camera entity to world)
+       matrix4x4_t viewmatrix;    ///< actual matrix for rendering (transforms to viewspace)
+       matrix4x4_t projectmatrix; ///< actual projection matrix (transforms from viewspace to screen)
        int x;
        int y;
        int z;
@@ -246,7 +247,7 @@ typedef struct r_viewport_s
        int height;
        int depth;
        r_viewport_type_t type;
-       float screentodepth[2]; // used by deferred renderer to calculate linear depth from device depth coordinates
+       float screentodepth[2];    ///< used by deferred renderer to calculate linear depth from device depth coordinates
 }
 r_viewport_t;
 
@@ -271,17 +272,17 @@ typedef struct r_refdef_view_s
        int numfrustumplanes;
        mplane_t frustum[6];
        qbool useclipplane;
-       qbool usecustompvs; // uses r_refdef.viewcache.pvsbits as-is rather than computing it
+       qbool usecustompvs; ///< uses r_refdef.viewcache.pvsbits as-is rather than computing it
        mplane_t clipplane;
        float frustum_x, frustum_y;
        vec3_t frustumcorner[4];
-       // if turned off it renders an ortho view
+       /// if turned off it renders an ortho view
        int useperspective;
-       // allows visibility culling based on the view origin (e.g. pvs and R_CanSeeBox)
-       // this is turned off by:
-       // r_trippy
-       // !r_refdef.view.useperspective
-       // (sometimes) r_refdef.view.useclipplane
+       /// allows visibility culling based on the view origin (e.g. pvs and R_CanSeeBox)
+       /// this is turned off by:
+       /// r_trippy
+       /// !r_refdef.view.useperspective
+       /// (sometimes) r_refdef.view.useclipplane
        int usevieworiginculling;
        float ortho_x, ortho_y;
 
@@ -292,19 +293,19 @@ typedef struct r_refdef_view_s
        int width;
        int height;
        int depth;
-       r_viewport_t viewport; // note: if r_viewscale is used, the viewport.width and viewport.height may be less than width and height
+       r_viewport_t viewport; ///< note: if r_viewscale is used, the viewport.width and viewport.height may be less than width and height
 
-       // which color components to allow (for anaglyph glasses)
+       /// which color components to allow (for anaglyph glasses)
        int colormask[4];
 
-       // global RGB color multiplier for rendering
+       /// global RGB color multiplier for rendering
        float colorscale;
 
-       // whether to call R_ClearScreen before rendering stuff
+       /// whether to call R_ClearScreen before rendering stuff
        qbool clear;
-       // if true, don't clear or do any post process effects (bloom, etc)
+       /// if true, don't clear or do any post process effects (bloom, etc)
        qbool isoverlay;
-       // if true, this is the MAIN view (which is, after CSQC, copied into the scene for use e.g. by r_speeds 1, showtex, prydon cursor)
+       /// if true, this is the MAIN view (which is, after CSQC, copied into the scene for use e.g. by r_speeds 1, showtex, prydon cursor)
        qbool ismain;
 
        // whether to draw r_showtris and such, this is only true for the main
@@ -316,7 +317,7 @@ typedef struct r_refdef_view_s
        int cullface_front;
        int cullface_back;
 
-       // render quality (0 to 1) - affects r_drawparticles_drawdistance and others
+       /// render quality (0 to 1) - affects r_drawparticles_drawdistance and others
        float quality;
 }
 r_refdef_view_t;
@@ -332,8 +333,8 @@ typedef struct r_refdef_viewcache_s
 
        // these properties are generated by R_View_Update()
 
-       // which entities are currently visible for this viewpoint
-       // (the used range is 0...r_refdef.scene.numentities)
+       /// which entities are currently visible for this viewpoint
+       /// (the used range is 0...r_refdef.scene.numentities)
        unsigned char *entityvisible;
 
        // flag arrays used for visibility checking on world model
@@ -341,7 +342,7 @@ typedef struct r_refdef_viewcache_s
        unsigned char *world_pvsbits;
        unsigned char *world_leafvisible;
        unsigned char *world_surfacevisible;
-       // if true, the view is currently in a leaf without pvs data
+       /// if true, the view is currently in a leaf without pvs data
        qbool world_novis;
 }
 r_refdef_viewcache_t;
@@ -349,24 +350,24 @@ r_refdef_viewcache_t;
 // TODO: really think about which fields should go into scene and which one should stay in refdef [1/7/2008 Black]
 // maybe also refactor some of the functions to support different setting sources (ie. fogenabled, etc.) for different scenes
 typedef struct r_refdef_scene_s {
-       // whether to call S_ExtraUpdate during render to reduce sound chop
+       /// whether to call S_ExtraUpdate during render to reduce sound chop
        qbool extraupdate;
 
-       // (client gameworld) time for rendering time based effects
+       /// (client gameworld) time for rendering time based effects
        double time;
 
-       // the world
+       /// the world
        entity_render_t *worldentity;
 
-       // same as worldentity->model
+       /// same as worldentity->model
        model_t *worldmodel;
 
-       // renderable entities (excluding world)
+       /// renderable entities (excluding world)
        entity_render_t **entities;
        int numentities;
        int maxentities;
 
-       // field of temporary entities that is reset each (client) frame
+       /// field of temporary entities that is reset each (client) frame
        entity_render_t *tempentities;
        int numtempentities;
        int maxtempentities;
@@ -378,10 +379,10 @@ typedef struct r_refdef_scene_s {
        int numlights;
 
        // intensities for light styles right now, controls rtlights
-       float rtlightstylevalue[MAX_LIGHTSTYLES];       // float fraction of base light value
+       float rtlightstylevalue[MAX_LIGHTSTYLES]; ///< float fraction of base light value
        // 8.8bit fixed point intensities for light styles
        // controls intensity lightmap layers
-       unsigned short lightstylevalue[MAX_LIGHTSTYLES];        // 8.8 fraction of base light value
+       unsigned short lightstylevalue[MAX_LIGHTSTYLES]; ///< 8.8 fraction of base light value
 
        // adds brightness to the whole scene, separate from lightmapintensity
        // see CL_UpdateEntityShading
@@ -554,8 +555,6 @@ extern cvar_t r_render;
 extern cvar_t r_renderview;
 extern cvar_t r_waterwarp;
 
-extern cvar_t r_textureunits;
-
 extern cvar_t r_glsl_offsetmapping;
 extern cvar_t r_glsl_offsetmapping_reliefmapping;
 extern cvar_t r_glsl_offsetmapping_scale;
@@ -593,7 +592,7 @@ void R_TimeReport(const char *name);
 void R_Stain(const vec3_t origin, float radius, int cr1, int cg1, int cb1, int ca1, int cr2, int cg2, int cb2, int ca2);
 
 void R_CalcBeam_Vertex3f(float *vert, const float *org1, const float *org2, float width);
-void R_CalcSprite_Vertex3f(float *vertex3f, const float *origin, const float *left, const float *up, float scalex1, float scalex2, float scaley1, float scaley2);
+void R_CalcSprite_Vertex3f(float *vertex3f, const vec3_t origin, const vec3_t left, const vec3_t up, float scalex1, float scalex2, float scaley1, float scaley2);
 
 extern mempool_t *r_main_mempool;
 
@@ -780,7 +779,7 @@ void R_HDR_UpdateIrisAdaptation(const vec3_t point);
 
 void RSurf_ActiveModelEntity(const entity_render_t *ent, qbool wantnormals, qbool wanttangents, qbool prepass);
 void RSurf_ActiveCustomEntity(const matrix4x4_t *matrix, const matrix4x4_t *inversematrix, int entflags, double shadertime, float r, float g, float b, float a, int numvertices, const float *vertex3f, const float *texcoord2f, const float *normal3f, const float *svector3f, const float *tvector3f, const float *color4f, int numtriangles, const int *element3i, const unsigned short *element3s, qbool wantnormals, qbool wanttangents);
-void RSurf_SetupDepthAndCulling(void);
+void RSurf_SetupDepthAndCulling(bool ui);
 
 extern int r_textureframe; ///< used only by R_GetCurrentTexture, incremented per view and per UI render
 texture_t *R_GetCurrentTexture(texture_t *t);
@@ -976,6 +975,7 @@ void R_Model_Sprite_Draw(entity_render_t *ent);
 struct prvm_prog_s;
 void R_UpdateFog(void);
 qbool CL_VM_UpdateView(double frametime);
+void CL_VM_DrawHud(double frametime);
 void SCR_DrawConsole(void);
 void R_Shadow_EditLights_DrawSelectedLightProperties(void);
 void R_DecalSystem_Reset(decalsystem_t *decalsystem);
diff --git a/sbar.c b/sbar.c
index be13963350a42847b088fb9afd0c7aa9427a8ea9..69707e448cbb54b87f5b11b13dcc34c8d509aec1 100644 (file)
--- a/sbar.c
+++ b/sbar.c
@@ -116,7 +116,6 @@ cvar_t crosshair_color_alpha = {CF_CLIENT | CF_ARCHIVE, "crosshair_color_alpha",
 cvar_t crosshair_size = {CF_CLIENT | CF_ARCHIVE, "crosshair_size", "1", "adjusts size of the crosshair on the screen"};
 
 static void Sbar_MiniDeathmatchOverlay (int x, int y);
-static void Sbar_DeathmatchOverlay (void);
 static void Sbar_IntermissionOverlay (void);
 static void Sbar_FinaleOverlay (void);
 
@@ -359,11 +358,8 @@ static void sbar_newmap(void)
 
 void Sbar_Init (void)
 {
-       if(gamemode == GAME_NORMAL) // Workaround so Quake doesn't trample on Xonotic.
-       {
-               Cmd_AddCommand(CF_CLIENT, "+showscores", Sbar_ShowScores_f, "show scoreboard");
-               Cmd_AddCommand(CF_CLIENT, "-showscores", Sbar_DontShowScores_f, "hide scoreboard");
-       }
+       Cmd_AddCommand(CF_CLIENT, "+showscores", Sbar_ShowScores_f, "show scoreboard");
+       Cmd_AddCommand(CF_CLIENT, "-showscores", Sbar_DontShowScores_f, "hide scoreboard");
        Cvar_RegisterVariable(&cl_showfps);
        Cvar_RegisterVariable(&cl_showsound);
        Cvar_RegisterVariable(&cl_showblur);
@@ -395,6 +391,8 @@ void Sbar_Init (void)
        Cvar_RegisterVariable(&sbar_miniscoreboard_size);
        Cvar_RegisterVariable(&sbar_info_pos);
        Cvar_RegisterVariable(&cl_deathscoreboard);
+       // This name is used by QuakeSpasm-based engines and is read by the Alkaline 1.2 CSQC
+       Cvar_RegisterVirtual(&sbar_alpha_bg, "scr_sbaralpha");
 
        Cvar_RegisterVariable(&crosshair_color_red);
        Cvar_RegisterVariable(&crosshair_color_green);
@@ -623,7 +621,7 @@ void Sbar_SortFrags (void)
                                                teamname = "Total Team Score";
                                                break;
                                }
-                               strlcpy(teams[teamlines-1].name, teamname, sizeof(teams[teamlines-1].name));
+                               dp_strlcpy(teams[teamlines-1].name, teamname, sizeof(teams[teamlines-1].name));
 
                                teams[teamlines-1].frags = 0;
                                teams[teamlines-1].colors = color + 16 * color;
@@ -685,7 +683,7 @@ static void Sbar_SoloScoreboard (void)
                Sbar_DrawString(8+22*8, 4, va(vabuf, sizeof(vabuf), "Secrets:%3i", cl.stats[STAT_SECRETS]));
 
        // format is like this: e1m1:The Sligpate Complex
-       dpsnprintf(str, sizeof(str), "%s:%s", cl.worldbasename, cl.worldmessage);
+       dpsnprintf(str, sizeof(str), "%s:%.39s", cl.worldbasename, cl.worldmessage);
 
        // if there's a newline character, terminate the string there
        if (strchr(str, '\n'))
@@ -894,7 +892,7 @@ static void Sbar_DrawInventory (void)
 
        // items
        for (i=0 ; i<6 ; i++)
-               if (cl.stats[STAT_ITEMS] & (1<<(17+i)))
+               if (cl.stats[STAT_ITEMS] & (1u<<(17+i)))
                {
                        //MED 01/04/97 changed keys
                        if (!(gamemode == GAME_HIPNOTIC || gamemode == GAME_QUOTH) || (i>1))
@@ -906,7 +904,7 @@ static void Sbar_DrawInventory (void)
        if (gamemode == GAME_HIPNOTIC || gamemode == GAME_QUOTH)
        {
                for (i=0 ; i<2 ; i++)
-                       if (cl.stats[STAT_ITEMS] & (1<<(24+i)))
+                       if (cl.stats[STAT_ITEMS] & (1u<<(24+i)))
                                Sbar_DrawPic (288 + i*16, -16, hsb_items[i]);
        }
 
@@ -914,14 +912,14 @@ static void Sbar_DrawInventory (void)
        {
                // new rogue items
                for (i=0 ; i<2 ; i++)
-                       if (cl.stats[STAT_ITEMS] & (1<<(29+i)))
+                       if (cl.stats[STAT_ITEMS] & (1u<<(29+i)))
                                Sbar_DrawPic (288 + i*16, -16, rsb_items[i]);
        }
        else
        {
                // sigils
                for (i=0 ; i<4 ; i++)
-                       if (cl.stats[STAT_ITEMS] & (1<<(28+i)))
+                       if (cl.stats[STAT_ITEMS] & (1u<<(28+i)))
                                Sbar_DrawPic (320-32 + i*8, -16, sb_sigil[i]);
        }
 }
@@ -1144,12 +1142,12 @@ void Sbar_ShowFPS(void)
        }
        if (cl_showtime.integer)
        {
-               strlcpy(timestring, Sys_TimeString(cl_showtime_format.string), sizeof(timestring));
+               dp_strlcpy(timestring, Sys_TimeString(cl_showtime_format.string), sizeof(timestring));
                fps_strings++;
        }
        if (cl_showdate.integer)
        {
-               strlcpy(datestring, Sys_TimeString(cl_showdate_format.string), sizeof(datestring));
+               dp_strlcpy(datestring, Sys_TimeString(cl_showdate_format.string), sizeof(datestring));
                fps_strings++;
        }
        if (cl_showblur.integer)
@@ -1217,13 +1215,13 @@ void Sbar_ShowFPS(void)
                svtrace.fraction = 2.0;
                cltrace.fraction = 2.0;
                // ray hits models (even animated ones) and ignores translucent materials
-               if (SVVM_prog != NULL)
+               if (sv.active)
                        svtrace = SV_TraceLine(org, dest, MOVE_HITMODEL, NULL, SUPERCONTENTS_SOLID, 0, MATERIALFLAGMASK_TRANSLUCENT, collision_extendmovelength.value);
                cltrace = CL_TraceLine(org, dest, MOVE_HITMODEL, NULL, SUPERCONTENTS_SOLID, 0, MATERIALFLAGMASK_TRANSLUCENT, collision_extendmovelength.value, true, false, &hitnetentity, true, true);
                if (cltrace.hittexture)
-                       strlcpy(texstring, cltrace.hittexture->name, sizeof(texstring));
+                       dp_strlcpy(texstring, cltrace.hittexture->name, sizeof(texstring));
                else
-                       strlcpy(texstring, "(no texture hit)", sizeof(texstring));
+                       dp_strlcpy(texstring, "(no texture hit)", sizeof(texstring));
                fps_strings++;
                if (svtrace.fraction < cltrace.fraction)
                {
@@ -1233,11 +1231,11 @@ void Sbar_ShowFPS(void)
                                dpsnprintf(entstring, sizeof(entstring), "server entity %i", (int)PRVM_EDICT_TO_PROG(svtrace.ent));
                        }
                        else
-                               strlcpy(entstring, "(no entity hit)", sizeof(entstring));
+                               dp_strlcpy(entstring, "(no entity hit)", sizeof(entstring));
                }
                else
                {
-                       if (CLVM_prog != NULL && cltrace.ent != NULL)
+                       if (cltrace.ent != NULL)
                        {
                                prvm_prog_t *prog = CLVM_prog;
                                dpsnprintf(entstring, sizeof(entstring), "client entity %i", (int)PRVM_EDICT_TO_PROG(cltrace.ent));
@@ -1245,9 +1243,9 @@ void Sbar_ShowFPS(void)
                        else if (hitnetentity > 0)
                                dpsnprintf(entstring, sizeof(entstring), "network entity %i", hitnetentity);
                        else if (hitnetentity == 0)
-                               strlcpy(entstring, "world entity", sizeof(entstring));
+                               dp_strlcpy(entstring, "world entity", sizeof(entstring));
                        else
-                               strlcpy(entstring, "(no entity hit)", sizeof(entstring));
+                               dp_strlcpy(entstring, "(no entity hit)", sizeof(entstring));
                }
                fps_strings++;
        }
diff --git a/sbar.h b/sbar.h
index 8f82d15b8dfcd8b34babab0adb2ee1776523fe7c..5de4c91d468368a4ea8b5c5304bec4bbdd2bc31b 100644 (file)
--- a/sbar.h
+++ b/sbar.h
@@ -38,5 +38,8 @@ void Sbar_ShowFPS_Update(void);
 int Sbar_GetSortedPlayerIndex (int index);
 void Sbar_SortFrags (void);
 
+extern cvar_t cl_deathscoreboard;
+void Sbar_DeathmatchOverlay (void);
+
 #endif
 
index 01d264ca72a7540eb8ce826b844009ea075e8d04..b018ad1eec10862788f2a04abdfc6a21d126de19 100644 (file)
--- a/screen.h
+++ b/screen.h
@@ -31,12 +31,9 @@ void CL_UpdateScreen (void);
 void SCR_CenterPrint(const char *str);
 
 void SCR_BeginLoadingPlaque (qbool startup);
+void SCR_DeferLoadingPlaque (qbool startup);
 void SCR_EndLoadingPlaque (void);
 
-// invoke refresh of loading plaque (nothing else seen)
-void SCR_UpdateLoadingScreen(qbool clear, qbool startup);
-void SCR_UpdateLoadingScreenIfShown(void);
-
 // pushes an item on the loading screen
 void SCR_PushLoadingScreen (const char *msg, float len_in_parent);
 void SCR_PopLoadingScreen (qbool redraw);
@@ -44,7 +41,7 @@ void SCR_ClearLoadingScreen (qbool redraw);
 
 void SCR_CaptureVideo_SoundFrame(const struct portable_samplepair_s *paintbuffer, size_t length);
 
-extern float scr_con_current; // current height of displayed console
+extern unsigned int scr_con_current; // current height of displayed console
 
 extern int sb_lines;
 
index 02d3786bb6319ec8450539e9695b7d15f69bb472..a3e0436bbbfcba02c6cc4a2f20318c6cd89262e1 100644 (file)
--- a/server.h
+++ b/server.h
@@ -34,20 +34,6 @@ typedef struct server_static_s
        qbool changelevel_issued;
        /// server infostring
        char serverinfo[MAX_SERVERINFO_STRING];
-       // performance data
-       float perf_cpuload;
-       float perf_lost;
-       float perf_offset_avg;
-       float perf_offset_max;
-       float perf_offset_sdev;
-       // temporary performance data accumulators
-       float perf_acc_realtime;
-       float perf_acc_sleeptime;
-       float perf_acc_lost;
-       float perf_acc_offset;
-       float perf_acc_offset_squared;
-       float perf_acc_offset_max;
-       int perf_acc_offset_samples;
 
        // csqc stuff
        unsigned char *csqc_progdata;
@@ -88,9 +74,25 @@ typedef struct server_s
        protocolversion_t protocol;
 
        double time;
-
        double frametime;
 
+       unsigned int spawnframe; // signals SV_Frame() to reset its timers
+
+       // performance data
+       float perf_cpuload;
+       float perf_lost;
+       float perf_offset_avg;
+       float perf_offset_max;
+       float perf_offset_sdev;
+       // temporary performance data accumulators
+       float perf_acc_realtime;
+       float perf_acc_sleeptime;
+       float perf_acc_lost;
+       float perf_acc_offset;
+       float perf_acc_offset_squared;
+       float perf_acc_offset_max;
+       int perf_acc_offset_samples;
+
        // used by PF_checkclient
        int lastcheck;
        double lastchecktime;
@@ -103,10 +105,7 @@ typedef struct server_s
        /// collision culling data
        world_t world;
 
-       /// map name
-       char name[64]; // %s followed by entrance name
        // variants of map name
-       char worldmessage[40]; // map title (not related to filename)
        char worldbasename[MAX_QPATH]; // %s
        char worldname[MAX_QPATH]; // maps/%s.bsp
        char worldnamenoextension[MAX_QPATH]; // maps/%s
@@ -415,6 +414,7 @@ extern cvar_t sv_allowdownloads_archive;
 extern cvar_t sv_allowdownloads_config;
 extern cvar_t sv_allowdownloads_dlcache;
 extern cvar_t sv_allowdownloads_inarchive;
+extern cvar_t sv_areagrid_link_SOLID_NOT;
 extern cvar_t sv_areagrid_mingridsize;
 extern cvar_t sv_checkforpacketsduringsleep;
 extern cvar_t sv_clmovement_enable;
@@ -469,6 +469,7 @@ extern cvar_t sv_gravity;
 extern cvar_t sv_idealpitchscale;
 extern cvar_t sv_jumpstep;
 extern cvar_t sv_jumpvelocity;
+extern cvar_t sv_legacy_bbox_expand;
 extern cvar_t sv_maxairspeed;
 extern cvar_t sv_maxrate;
 extern cvar_t sv_maxspeed;
@@ -566,10 +567,6 @@ void SV_LinkEdict_TouchAreaGrid_Call(prvm_edict_t *touch, prvm_edict_t *ent); //
  * returns true if it found a better place
  */
 qbool SV_UnstickEntity (prvm_edict_t *ent);
-/*! move an entity that is stuck out of the surface it is stuck in (can move large amounts)
- * returns true if it found a better place
- */
-qbool SV_NudgeOutOfSolid(prvm_edict_t *ent);
 
 /// calculates hitsupercontentsmask for a generic qc entity
 int SV_GenericHitSuperContentsMask(const prvm_edict_t *edict);
@@ -581,7 +578,7 @@ int SV_EntitiesInBox(const vec3_t mins, const vec3_t maxs, int maxedicts, prvm_e
 
 qbool SV_CanSeeBox(int numsamples, vec_t eyejitter, vec_t enlarge, vec_t entboxexpand, vec3_t eye, vec3_t entboxmins, vec3_t entboxmaxs);
 
-void SV_MarkWriteEntityStateToClient(entity_state_t *s);
+void SV_MarkWriteEntityStateToClient(entity_state_t *s, client_t *client);
 
 void SV_SendServerinfo(client_t *client);
 void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t *msg, int maxsize);
@@ -626,6 +623,6 @@ void SV_PreSpawn_f(cmd_state_t *cmd);
 void SV_Spawn_f(cmd_state_t *cmd);
 void SV_Begin_f(cmd_state_t *cmd);
 
-qbool SV_VM_ConsoleCommand (const char *text);
+qbool SV_VM_ConsoleCommand(const char *text, size_t textlen);
 
 #endif
index ebf84b9bfd599e29601877a9e12971fa4116b872..03d7e33e76ae59c9892810a11809bb2532dac5b3 100644 (file)
@@ -183,6 +183,7 @@ cvar_t snd_spatialization_prologic_frontangle = {CF_CLIENT | CF_ARCHIVE, "snd_sp
 cvar_t snd_spatialization_occlusion = {CF_CLIENT | CF_ARCHIVE, "snd_spatialization_occlusion", "1", "enable occlusion testing on spatialized sounds, which simply quiets sounds that are blocked by the world; 1 enables PVS method, 2 enables LineOfSight method, 3 enables both"};
 
 // Cvars declared in snd_main.h (shared with other snd_*.c files)
+cvar_t snd_waterfx = {CF_CLIENT | CF_ARCHIVE, "snd_waterfx", "1", "underwater sound filter strength"};
 cvar_t _snd_mixahead = {CF_CLIENT | CF_ARCHIVE, "_snd_mixahead", "0.15", "how much sound to mix ahead of time"};
 cvar_t snd_streaming = {CF_CLIENT | CF_ARCHIVE, "snd_streaming", "1", "enables keeping compressed ogg sound files compressed, decompressing them only as needed, otherwise they will be decompressed completely at load (may use a lot of memory); when set to 2, streaming is performed even if this would waste memory"};
 cvar_t snd_streaming_length = {CF_CLIENT | CF_ARCHIVE, "snd_streaming_length", "1", "decompress sounds completely if they are less than this play time when snd_streaming is 1"};
@@ -276,9 +277,9 @@ static void S_Play_Common (cmd_state_t *cmd, float fvol, float attenuation)
        while (i < Cmd_Argc (cmd))
        {
                // Get the name, and appends ".wav" as an extension if there's none
-               strlcpy (name, Cmd_Argv(cmd, i), sizeof (name));
+               dp_strlcpy (name, Cmd_Argv(cmd, i), sizeof (name));
                if (!strrchr (name, '.'))
-                       strlcat (name, ".wav", sizeof (name));
+                       dp_strlcat (name, ".wav", sizeof (name));
                i++;
 
                // If we need to get the volume from the command line
@@ -661,6 +662,8 @@ void S_Startup (void)
 #ifdef CONFIG_VIDEO_CAPTURE
        recording_sound = false;
 #endif
+
+       CDAudio_Startup();
 }
 
 void S_Shutdown(void)
@@ -668,6 +671,8 @@ void S_Shutdown(void)
        if (snd_renderbuffer == NULL)
                return;
 
+       CDAudio_Shutdown();
+
        oldpaintedtime = snd_renderbuffer->endframe;
 
        if (simsound)
@@ -808,6 +813,7 @@ void S_Init(void)
        Cvar_RegisterVariable(&ambient_fade);
        Cvar_RegisterVariable(&snd_noextraupdate);
        Cvar_RegisterVariable(&snd_show);
+       Cvar_RegisterVariable(&snd_waterfx);
        Cvar_RegisterVariable(&_snd_mixahead);
        Cvar_RegisterVariable(&snd_swapstereo); // for people with backwards sound wiring
        Cvar_RegisterVariable(&snd_channellayout);
@@ -824,6 +830,8 @@ void S_Init(void)
 #ifdef USEXMP
        XMP_OpenLibrary ();
 #endif
+
+       CDAudio_Init();
 }
 
 
@@ -921,7 +929,7 @@ sfx_t *S_FindName (const char *name)
        // Add a sfx_t struct for this sound
        sfx = (sfx_t *)Mem_Alloc (snd_mempool, sizeof (*sfx));
        memset (sfx, 0, sizeof(*sfx));
-       strlcpy (sfx->name, name, sizeof (sfx->name));
+       dp_strlcpy (sfx->name, name, sizeof (sfx->name));
        sfx->memsize = sizeof(*sfx);
        sfx->next = known_sfx;
        known_sfx = sfx;
@@ -1220,7 +1228,7 @@ static void SND_Spatialize_WithSfx(channel_t *ch, qbool isstatic, sfx_t *sfx)
                {
                        //Con_Printf("-- entnum %i origin %f %f %f neworigin %f %f %f\n", ch->entnum, ch->origin[0], ch->origin[1], ch->origin[2], cl.entities[ch->entnum].state_current.origin[0], cl.entities[ch->entnum].state_current.origin[1], cl.entities[ch->entnum].state_current.origin[2]);
 
-                       if (ch->entnum > MAX_EDICTS)
+                       if (CLVM_prog->loaded && ch->entnum > MAX_EDICTS)
                                if (!CL_VM_GetEntitySoundOrigin(ch->entnum, ch->origin))
                                        ch->entnum = MAX_EDICTS; // entity was removed, disown sound
                }
@@ -1234,7 +1242,7 @@ static void SND_Spatialize_WithSfx(channel_t *ch, qbool isstatic, sfx_t *sfx)
                        else
                                Matrix4x4_OriginFromMatrix(&cl.entities[ch->entnum].render.matrix, ch->origin);
                }
-               else if (cl.csqc_server2csqcentitynumber[ch->entnum])
+               else if (CLVM_prog->loaded && cl.csqc_server2csqcentitynumber[ch->entnum])
                {
                        //Con_Printf("-- entnum %i (client %i) origin %f %f %f neworigin %f %f %f\n", ch->entnum, cl.csqc_server2csqcentitynumber[ch->entnum], ch->origin[0], ch->origin[1], ch->origin[2], cl.entities[ch->entnum].state_current.origin[0], cl.entities[ch->entnum].state_current.origin[1], cl.entities[ch->entnum].state_current.origin[2]);
 
@@ -1844,7 +1852,6 @@ void S_StaticSound (sfx_t *sfx, vec3_t origin, float fvol, float attenuation)
        S_PlaySfxOnChannel (sfx, target_chan, CHANNELFLAG_FORCELOOP, origin, fvol, attenuation, true, 0, 0, 0, 1.0f);
 }
 
-
 /*
 ===================
 S_UpdateAmbientSounds
@@ -1864,7 +1871,10 @@ static void S_UpdateAmbientSounds (void)
        if (cl.worldmodel && cl.worldmodel->brush.AmbientSoundLevelsForPoint)
                cl.worldmodel->brush.AmbientSoundLevelsForPoint(cl.worldmodel, listener_origin, ambientlevels, sizeof(ambientlevels));
 
+
        // Calc ambient sound levels
+       S_SetUnderwaterIntensity();
+
        for (ambient_channel = 0 ; ambient_channel< NUM_AMBIENTS ; ambient_channel++)
        {
                chan = &channels[ambient_channel];
index ff5e43f188a37f0aeef8ba4fbfaff6c2583024d2..1ce9e1693ebf1c5a4ef31d0be142ebce90e61ac6 100644 (file)
@@ -125,6 +125,7 @@ extern snd_ringbuffer_t *snd_renderbuffer;
 extern qbool snd_threaded; // enables use of snd_usethreadedmixing, provided that no sound hacks are in effect (like timedemo)
 extern qbool snd_usethreadedmixing; // if true, the main thread does not mix sound, soundtime does not advance, and neither does snd_renderbuffer->endframe, instead the audio thread will call S_MixToBuffer as needed
 
+extern struct cvar_s snd_waterfx;
 extern struct cvar_s _snd_mixahead;
 extern struct cvar_s snd_swapstereo;
 extern struct cvar_s snd_streaming;
@@ -152,6 +153,8 @@ extern qbool simsound;
 //         Architecture-independent functions
 // ====================================================================
 
+void S_SetUnderwaterIntensity(void);
+
 void S_MixToBuffer(void *stream, unsigned int frames);
 
 qbool S_LoadSound (struct sfx_s *sfx, qbool complain);
index 736d6ea1964f54ab7d11485daafde3d5d498f039..d8ea1dbe3764a297ead5aef44712325d5d901ee4 100644 (file)
--- a/snd_mix.c
+++ b/snd_mix.c
@@ -310,6 +310,66 @@ static void S_ConvertPaintBuffer(portable_sampleframe_t *painted_ptr, void *rb_p
 }
 
 
+
+/*
+===============================================================================
+
+UNDERWATER EFFECT
+
+Muffles the intensity of sounds when the player is underwater
+
+===============================================================================
+*/
+
+static struct
+{
+       float intensity;
+       float alpha;
+       float accum[SND_LISTENERS];
+}
+underwater = {0.f, 1.f, {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}};
+
+void S_SetUnderwaterIntensity(void)
+{
+       float target = cl.view_underwater ? bound(0.f, snd_waterfx.value, 2.f) : 0.f;
+
+       if (underwater.intensity < target)
+       {
+               underwater.intensity += cl.realframetime * 4.f;
+               underwater.intensity = min(underwater.intensity, target);
+       }
+       else if (underwater.intensity > target)
+       {
+               underwater.intensity -= cl.realframetime * 4.f;
+               underwater.intensity = max(underwater.intensity, target);
+       }
+
+       underwater.alpha = underwater.intensity ? exp(-underwater.intensity * log(12.f)) : 1.f;
+}
+
+static void S_UnderwaterFilter(int endtime)
+{
+       int i;
+       int sl;
+
+       if (!underwater.intensity)
+       {
+               if (endtime > 0)
+                       for (sl = 0; sl < SND_LISTENERS; sl++)
+                               underwater.accum[sl] = paintbuffer[endtime-1].sample[sl];
+               return;
+       }
+
+       for (i = 0; i < endtime; i++)
+               for (sl = 0; sl < SND_LISTENERS; sl++)
+               {
+                       underwater.accum[sl] += underwater.alpha * (paintbuffer[i].sample[sl] - underwater.accum[sl]);
+                       paintbuffer[i].sample[sl] = underwater.accum[sl];
+               }
+}
+
+
+
 /*
 ===============================================================================
 
@@ -580,6 +640,9 @@ void S_MixToBuffer(void *stream, unsigned int bufferframes)
 
                S_SoftClipPaintBuffer(paintbuffer, totalmixframes, snd_renderbuffer->format.width, snd_renderbuffer->format.channels);
 
+               S_UnderwaterFilter(totalmixframes);
+
+
 #ifdef CONFIG_VIDEO_CAPTURE
                if (!snd_usethreadedmixing)
                        S_CaptureAVISound(paintbuffer, totalmixframes);
index 036f4c63f1588cae58155a02479a9ec77f9ae1b7..138b2c09c0c78cecf275a77f2172795b19796d2e 100644 (file)
@@ -49,8 +49,6 @@ command from the console.  Active clients are kicked off.
 */
 static void SV_Map_f(cmd_state_t *cmd)
 {
-       char level[MAX_QPATH];
-
        if (Cmd_Argc(cmd) != 2)
        {
                Con_Print("map <levelname> : start a new game (kicks off all players)\n");
@@ -78,8 +76,7 @@ static void SV_Map_f(cmd_state_t *cmd)
                host.hook.ToggleMenu();
 
        svs.serverflags = 0;                    // haven't completed an episode yet
-       strlcpy(level, Cmd_Argv(cmd, 1), sizeof(level));
-       SV_SpawnServer(level);
+       SV_SpawnServer(Cmd_Argv(cmd, 1));
 
        if(sv.active && host.hook.ConnectLocal != NULL)
                host.hook.ConnectLocal();
@@ -94,8 +91,6 @@ Goes to a new map, taking all clients along
 */
 static void SV_Changelevel_f(cmd_state_t *cmd)
 {
-       char level[MAX_QPATH];
-
        if (Cmd_Argc(cmd) != 2)
        {
                Con_Print("changelevel <levelname> : continue game on a new level\n");
@@ -104,7 +99,7 @@ static void SV_Changelevel_f(cmd_state_t *cmd)
 
        if (!sv.active)
        {
-               Con_Printf("You must be running a server to changelevel. Use 'map %s' instead\n", Cmd_Argv(cmd, 1));
+               SV_Map_f(cmd);
                return;
        }
 
@@ -112,8 +107,7 @@ static void SV_Changelevel_f(cmd_state_t *cmd)
                host.hook.ToggleMenu();
 
        SV_SaveSpawnparms ();
-       strlcpy(level, Cmd_Argv(cmd, 1), sizeof(level));
-       SV_SpawnServer(level);
+       SV_SpawnServer(Cmd_Argv(cmd, 1));
        
        if(sv.active && host.hook.ConnectLocal != NULL)
                host.hook.ConnectLocal();
@@ -128,8 +122,6 @@ Restarts the current server for a dead player
 */
 static void SV_Restart_f(cmd_state_t *cmd)
 {
-       char mapname[MAX_QPATH];
-
        if (Cmd_Argc(cmd) != 1)
        {
                Con_Print("restart : restart current level\n");
@@ -144,8 +136,7 @@ static void SV_Restart_f(cmd_state_t *cmd)
        if(host.hook.ToggleMenu)
                host.hook.ToggleMenu();
 
-       strlcpy(mapname, sv.name, sizeof(mapname));
-       SV_SpawnServer(mapname);
+       SV_SpawnServer(sv.worldbasename);
        
        if(sv.active && host.hook.ConnectLocal != NULL)
                host.hook.ConnectLocal();
@@ -157,11 +148,11 @@ static void SV_Restart_f(cmd_state_t *cmd)
 static void SV_DisableCheats_c(cvar_t *var)
 {
        prvm_prog_t *prog = SVVM_prog;
-       int i = 0;
+       int i;
 
-       if (var->value == 0)
+       if (prog->loaded && var->value == 0)
        {
-               while (svs.clients[i].edict)
+               for (i = 0; i < svs.maxclients; ++i)
                {
                        if (((int)PRVM_serveredictfloat(svs.clients[i].edict, flags) & FL_GODMODE))
                                PRVM_serveredictfloat(svs.clients[i].edict, flags) = (int)PRVM_serveredictfloat(svs.clients[i].edict, flags) ^ FL_GODMODE;
@@ -173,7 +164,6 @@ static void SV_DisableCheats_c(cvar_t *var)
                                noclip_anglehack = false;
                                PRVM_serveredictfloat(svs.clients[i].edict, movetype) = MOVETYPE_WALK;
                        }
-                       i++;
                }
        }
 }
@@ -469,7 +459,7 @@ static void SV_Say(cmd_state_t *cmd, qbool teamonly)
                p2[-1] = 0;
                p2--;
        }
-       strlcat(text, "\n", sizeof(text));
+       dp_strlcat(text, "\n", sizeof(text));
 
        // note: save is not a valid edict if fromServer is true
        save = host_client;
@@ -736,10 +726,11 @@ static void SV_Status_f(cmd_state_t *cmd)
        for (players = 0, i = 0;i < svs.maxclients;i++)
                if (svs.clients[i].active)
                        players++;
+
        print ("host:     %s\n", Cvar_VariableString (&cvars_all, "hostname", CF_SERVER));
-       print ("version:  %s build %s (gamename %s)\n", gamename, buildstring, gamenetworkfiltername);
+       print ("version:  %s\n", engineversion);
        print ("protocol: %i (%s)\n", Protocol_NumberForEnum(sv.protocol), Protocol_NameForEnum(sv.protocol));
-       print ("map:      %s\n", sv.name);
+       print ("map:      %s\n", sv.worldbasename);
        print ("timing:   %s\n", SV_TimingReport(vabuf, sizeof(vabuf)));
        print ("players:  %i active (%i max)\n\n", players, svs.maxclients);
 
@@ -779,9 +770,9 @@ static void SV_Status_f(cmd_state_t *cmd)
                }
 
                if(sv_status_privacy.integer && cmd->source != src_local && LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
-                       strlcpy(ip, client->netconnection ? "hidden" : "botclient", 48);
+                       dp_strlcpy(ip, client->netconnection ? "hidden" : "botclient", 48);
                else
-                       strlcpy(ip, (client->netconnection && *client->netconnection->address) ? client->netconnection->address : "botclient", 48);
+                       dp_strlcpy(ip, (client->netconnection && *client->netconnection->address) ? client->netconnection->address : "botclient", 48);
 
                frags = client->frags;
 
@@ -837,7 +828,7 @@ void SV_Name(int clientnum)
        {
                if (host_client->begun)
                        SV_BroadcastPrintf("\003%s ^7changed name to ^3%s\n", host_client->old_name, host_client->name);
-               strlcpy(host_client->old_name, host_client->name, sizeof(host_client->old_name));
+               dp_strlcpy(host_client->old_name, host_client->name, sizeof(host_client->old_name));
                // send notification to all clients
                MSG_WriteByte (&sv.reliable_datagram, svc_updatename);
                MSG_WriteByte (&sv.reliable_datagram, clientnum);
@@ -866,7 +857,7 @@ static void SV_Name_f(cmd_state_t *cmd)
        else
                newNameSource = Cmd_Args(cmd);
 
-       strlcpy(newName, newNameSource, sizeof(newName));
+       dp_strlcpy(newName, newNameSource, sizeof(newName));
 
        if (cmd->source == src_local)
                return;
@@ -880,7 +871,7 @@ static void SV_Name_f(cmd_state_t *cmd)
        host_client->nametime = host.realtime + max(0.0f, sv_namechangetimer.value);
 
        // point the string back at updateclient->name to keep it safe
-       strlcpy (host_client->name, newName, sizeof (host_client->name));
+       dp_strlcpy (host_client->name, newName, sizeof (host_client->name));
 
        for (i = 0, j = 0;host_client->name[i];i++)
                if (host_client->name[i] != '\r' && host_client->name[i] != '\n')
@@ -1138,9 +1129,9 @@ static void SV_Playermodel_f(cmd_state_t *cmd)
                return;
 
        if (Cmd_Argc (cmd) == 2)
-               strlcpy (newPath, Cmd_Argv(cmd, 1), sizeof (newPath));
+               dp_strlcpy (newPath, Cmd_Argv(cmd, 1), sizeof (newPath));
        else
-               strlcpy (newPath, Cmd_Args(cmd), sizeof (newPath));
+               dp_strlcpy (newPath, Cmd_Args(cmd), sizeof (newPath));
 
        for (i = 0, j = 0;newPath[i];i++)
                if (newPath[i] != '\r' && newPath[i] != '\n')
@@ -1158,11 +1149,11 @@ static void SV_Playermodel_f(cmd_state_t *cmd)
        */
 
        // point the string back at updateclient->name to keep it safe
-       strlcpy (host_client->playermodel, newPath, sizeof (host_client->playermodel));
+       dp_strlcpy (host_client->playermodel, newPath, sizeof (host_client->playermodel));
        PRVM_serveredictstring(host_client->edict, playermodel) = PRVM_SetEngineString(prog, host_client->playermodel);
        if (strcmp(host_client->old_model, host_client->playermodel))
        {
-               strlcpy(host_client->old_model, host_client->playermodel, sizeof(host_client->old_model));
+               dp_strlcpy(host_client->old_model, host_client->playermodel, sizeof(host_client->old_model));
                /*// send notification to all clients
                MSG_WriteByte (&sv.reliable_datagram, svc_updatepmodel);
                MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
@@ -1185,9 +1176,9 @@ static void SV_Playerskin_f(cmd_state_t *cmd)
                return;
 
        if (Cmd_Argc (cmd) == 2)
-               strlcpy (newPath, Cmd_Argv(cmd, 1), sizeof (newPath));
+               dp_strlcpy (newPath, Cmd_Argv(cmd, 1), sizeof (newPath));
        else
-               strlcpy (newPath, Cmd_Args(cmd), sizeof (newPath));
+               dp_strlcpy (newPath, Cmd_Args(cmd), sizeof (newPath));
 
        for (i = 0, j = 0;newPath[i];i++)
                if (newPath[i] != '\r' && newPath[i] != '\n')
@@ -1205,13 +1196,13 @@ static void SV_Playerskin_f(cmd_state_t *cmd)
        */
 
        // point the string back at updateclient->name to keep it safe
-       strlcpy (host_client->playerskin, newPath, sizeof (host_client->playerskin));
+       dp_strlcpy (host_client->playerskin, newPath, sizeof (host_client->playerskin));
        PRVM_serveredictstring(host_client->edict, playerskin) = PRVM_SetEngineString(prog, host_client->playerskin);
        if (strcmp(host_client->old_skin, host_client->playerskin))
        {
                //if (host_client->begun)
                //      SV_BroadcastPrintf("%s changed skin to %s\n", host_client->name, host_client->playerskin);
-               strlcpy(host_client->old_skin, host_client->playerskin, sizeof(host_client->old_skin));
+               dp_strlcpy(host_client->old_skin, host_client->playerskin, sizeof(host_client->old_skin));
                /*// send notification to all clients
                MSG_WriteByte (&sv.reliable_datagram, svc_updatepskin);
                MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
@@ -1613,7 +1604,7 @@ void SV_InitOperatorCommands(void)
        Cmd_AddCommand(CF_SERVER | CF_SERVER_FROM_CLIENT, "status", SV_Status_f, "print server status information");
        Cmd_AddCommand(CF_SHARED, "map", SV_Map_f, "kick everyone off the server and start a new level");
        Cmd_AddCommand(CF_SHARED, "restart", SV_Restart_f, "restart current level");
-       Cmd_AddCommand(CF_SHARED, "changelevel", SV_Changelevel_f, "change to another level, bringing along all connected clients");
+       Cmd_AddCommand(CF_SHARED, "changelevel", SV_Changelevel_f, "change to another level, bringing along all connected clients (or start a new level if none is loaded)");
        Cmd_AddCommand(CF_SHARED | CF_SERVER_FROM_CLIENT, "say", SV_Say_f, "send a chat message to everyone on the server");
        Cmd_AddCommand(CF_SERVER_FROM_CLIENT, "say_team", SV_Say_Team_f, "send a chat message to your team on the server");
        Cmd_AddCommand(CF_SHARED | CF_SERVER_FROM_CLIENT, "tell", SV_Tell_f, "send a chat message to only one person on the server");
index 08ebe8411bb0e8615fef4bed77fd99d8ce0a8479..de267620568d8b404ac39e24be2488c7c9ecdc20 100644 (file)
--- a/sv_demo.c
+++ b/sv_demo.c
@@ -11,7 +11,7 @@ void SV_StartDemoRecording(client_t *client, const char *filename, int forcetrac
        if(client->sv_demo_file != NULL)
                return; // we already have a demo
 
-       strlcpy(name, filename, sizeof(name));
+       dp_strlcpy(name, filename, sizeof(name));
        FS_DefaultExtension(name, ".dem", sizeof(name));
 
        Con_Printf("Recording demo for # %d (%s) to %s\n", PRVM_NUM_FOR_EDICT(client->edict), client->netaddress, name);
index 8dd9db4d944c5307f585d5248ffe2254fee091cc..895601fa77a63178316c6139f788f8e2a47d7112 100644 (file)
--- a/sv_ents.c
+++ b/sv_ents.c
@@ -395,7 +395,7 @@ void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t *
        sv.sententitiesmark++;
 
        for (i = 0;i < sv.numsendentities;i++)
-               SV_MarkWriteEntityStateToClient(sv.sendentities + i);
+               SV_MarkWriteEntityStateToClient(sv.sendentities + i, client);
 
        numsendstates = 0;
        numcsqcsendstates = 0;
index ba0c5d525e3416d05719684870a7120802f7e0a7..f35abf3fe2aba1764441059aae8d740ceb740436 100644 (file)
--- a/sv_main.c
+++ b/sv_main.c
@@ -72,6 +72,7 @@ cvar_t sv_allowdownloads_archive = {CF_SERVER, "sv_allowdownloads_archive", "0",
 cvar_t sv_allowdownloads_config = {CF_SERVER, "sv_allowdownloads_config", "0", "whether to allow downloads of config files (cfg)"};
 cvar_t sv_allowdownloads_dlcache = {CF_SERVER, "sv_allowdownloads_dlcache", "0", "whether to allow downloads of dlcache files (dlcache/)"};
 cvar_t sv_allowdownloads_inarchive = {CF_SERVER, "sv_allowdownloads_inarchive", "0", "whether to allow downloads from archives (pak/pk3)"};
+cvar_t sv_areagrid_link_SOLID_NOT = {CF_SERVER | CF_NOTIFY, "sv_areagrid_link_SOLID_NOT", "1", "set to 0 to prevent SOLID_NOT entities from being linked to the area grid, and unlink any that are already linked (in the code paths that would otherwise link them), for better performance"};
 cvar_t sv_areagrid_mingridsize = {CF_SERVER | CF_NOTIFY, "sv_areagrid_mingridsize", "128", "minimum areagrid cell size, smaller values work better for lots of small objects, higher values for large objects"};
 cvar_t sv_checkforpacketsduringsleep = {CF_SERVER, "sv_checkforpacketsduringsleep", "0", "uses select() function to wait between frames which can be interrupted by packets being received, instead of Sleep()/usleep()/SDL_Sleep() functions which do not check for packets"};
 cvar_t sv_clmovement_enable = {CF_SERVER, "sv_clmovement_enable", "1", "whether to allow clients to use cl_movement prediction, which can cause choppy movement on the server which may annoy other players"};
@@ -82,17 +83,18 @@ cvar_t sv_cullentities_nevercullbmodels = {CF_SERVER, "sv_cullentities_nevercull
 cvar_t sv_cullentities_pvs = {CF_SERVER, "sv_cullentities_pvs", "1", "fast but loose culling of hidden entities"};
 cvar_t sv_cullentities_stats = {CF_SERVER, "sv_cullentities_stats", "0", "displays stats on network entities culled by various methods for each client"};
 cvar_t sv_cullentities_trace = {CF_SERVER, "sv_cullentities_trace", "0", "somewhat slow but very tight culling of hidden entities, minimizes network traffic and makes wallhack cheats useless"};
-cvar_t sv_cullentities_trace_delay = {CF_SERVER, "sv_cullentities_trace_delay", "1", "number of seconds until the entity gets actually culled"};
+cvar_t sv_cullentities_trace_delay = {CF_SERVER, "sv_cullentities_trace_delay", "1", "number of seconds until the entity gets actually culled (also applies to portal camera eyes even if sv_cullentities_trace is 0)"};
 cvar_t sv_cullentities_trace_delay_players = {CF_SERVER, "sv_cullentities_trace_delay_players", "0.2", "number of seconds until the entity gets actually culled if it is a player entity"};
-cvar_t sv_cullentities_trace_enlarge = {CF_SERVER, "sv_cullentities_trace_enlarge", "0", "box enlargement for entity culling"};
-cvar_t sv_cullentities_trace_expand = {CF_SERVER, "sv_cullentities_trace_expand", "0", "box is expanded by this many units for entity culling"};
-cvar_t sv_cullentities_trace_eyejitter = {CF_SERVER, "sv_cullentities_trace_eyejitter", "16", "jitter the eye by this much for each trace"};
+cvar_t sv_cullentities_trace_enlarge = {CF_SERVER, "sv_cullentities_trace_enlarge", "0", "box enlargement for entity culling (also applies to portal camera eyes even if sv_cullentities_trace is 0)"};
+cvar_t sv_cullentities_trace_expand = {CF_SERVER, "sv_cullentities_trace_expand", "0", "box is expanded by this many units for entity culling (also applies to portal camera eyes even if sv_cullentities_trace is 0)"};
+cvar_t sv_cullentities_trace_eyejitter = {CF_SERVER, "sv_cullentities_trace_eyejitter", "16", "jitter the eye by this much for each trace (also applies to portal camera eyes even if sv_cullentities_trace is 0)"};
 cvar_t sv_cullentities_trace_prediction = {CF_SERVER, "sv_cullentities_trace_prediction", "1", "also trace from the predicted player position"};
-cvar_t sv_cullentities_trace_prediction_time = {CF_SERVER, "sv_cullentities_trace_prediction_time", "0.2", "how many seconds of prediction to use"};
+cvar_t sv_cullentities_trace_prediction_time = {CF_SERVER, "sv_cullentities_trace_prediction_time", "0.2", "maximum ping time to predict in seconds"};
 cvar_t sv_cullentities_trace_entityocclusion = {CF_SERVER, "sv_cullentities_trace_entityocclusion", "0", "also check if doors and other bsp models are in the way"};
 cvar_t sv_cullentities_trace_samples = {CF_SERVER, "sv_cullentities_trace_samples", "2", "number of samples to test for entity culling"};
-cvar_t sv_cullentities_trace_samples_extra = {CF_SERVER, "sv_cullentities_trace_samples_extra", "2", "number of samples to test for entity culling when the entity affects its surroundings by e.g. dlight"};
+cvar_t sv_cullentities_trace_samples_extra = {CF_SERVER, "sv_cullentities_trace_samples_extra", "2", "number of samples to test for entity culling when the entity affects its surroundings by e.g. dlight (also applies to portal camera eyes even if sv_cullentities_trace is 0)"};
 cvar_t sv_cullentities_trace_samples_players = {CF_SERVER, "sv_cullentities_trace_samples_players", "8", "number of samples to test for entity culling when the entity is a player entity"};
+cvar_t sv_cullentities_trace_spectators = {CF_SERVER, "sv_cullentities_trace_spectators", "0", "enables trace entity culling for clients that are spectating"};
 cvar_t sv_debugmove = {CF_SERVER | CF_NOTIFY, "sv_debugmove", "0", "disables collision detection optimizations for debugging purposes"};
 cvar_t sv_echobprint = {CF_SERVER | CF_ARCHIVE, "sv_echobprint", "1", "prints gamecode bprint() calls to server console"};
 cvar_t sv_edgefriction = {CF_SERVER, "edgefriction", "1", "how much you slow down when nearing a ledge you might fall off, multiplier of sv_friction (Quake used 2, QuakeWorld used 1 due to a bug in physics code)"};
@@ -111,7 +113,7 @@ cvar_t sv_gameplayfix_grenadebouncedownslopes = {CF_SERVER, "sv_gameplayfix_gren
 cvar_t sv_gameplayfix_multiplethinksperframe = {CF_SERVER, "sv_gameplayfix_multiplethinksperframe", "1", "allows entities to think more often than the server framerate, primarily useful for very high fire rate weapons"};
 cvar_t sv_gameplayfix_noairborncorpse = {CF_SERVER, "sv_gameplayfix_noairborncorpse", "1", "causes entities (corpses, items, etc) sitting ontop of moving entities (players) to fall when the moving entity (player) is no longer supporting them"};
 cvar_t sv_gameplayfix_noairborncorpse_allowsuspendeditems = {CF_SERVER, "sv_gameplayfix_noairborncorpse_allowsuspendeditems", "1", "causes entities sitting ontop of objects that are instantaneously remove to float in midair (special hack to allow a common level design trick for floating items)"};
-cvar_t sv_gameplayfix_nudgeoutofsolid = {CF_SERVER, "sv_gameplayfix_nudgeoutofsolid", "0", "attempts to fix physics errors (where an object ended up in solid for some reason)"};
+cvar_t sv_gameplayfix_nudgeoutofsolid = {CF_SERVER, "sv_gameplayfix_nudgeoutofsolid", "1", "attempts to fix physics errors where an object ended up in solid for some reason, supersedes sv_gameplayfix_unstickentities"};
 cvar_t sv_gameplayfix_nudgeoutofsolid_separation = {CF_SERVER, "sv_gameplayfix_nudgeoutofsolid_separation", "0.03125", "keep objects this distance apart to prevent collision issues on seams"};
 cvar_t sv_gameplayfix_q2airaccelerate = {CF_SERVER, "sv_gameplayfix_q2airaccelerate", "0", "Quake2-style air acceleration"};
 cvar_t sv_gameplayfix_nogravityonground = {CF_SERVER, "sv_gameplayfix_nogravityonground", "0", "turn off gravity when on ground (to get rid of sliding)"};
@@ -122,17 +124,17 @@ cvar_t sv_gameplayfix_stepmultipletimes = {CF_SERVER, "sv_gameplayfix_stepmultip
 cvar_t sv_gameplayfix_nostepmoveonsteepslopes = {CF_SERVER, "sv_gameplayfix_nostepmoveonsteepslopes", "0", "crude fix which prevents MOVETYPE_STEP (not swimming or flying) to move on slopes whose angle is bigger than 45 degree"};
 cvar_t sv_gameplayfix_swiminbmodels = {CF_SERVER, "sv_gameplayfix_swiminbmodels", "1", "causes pointcontents (used to determine if you are in a liquid) to check bmodel entities as well as the world model, so you can swim around in (possibly moving) water bmodel entities"};
 cvar_t sv_gameplayfix_upwardvelocityclearsongroundflag = {CF_SERVER, "sv_gameplayfix_upwardvelocityclearsongroundflag", "1", "prevents monsters, items, and most other objects from being stuck to the floor when pushed around by damage, and other situations in mods"};
-cvar_t sv_gameplayfix_downtracesupportsongroundflag = {CF_SERVER, "sv_gameplayfix_downtracesupportsongroundflag", "1", "prevents very short moves from clearing onground (which may make the player stick to the floor at high netfps)"};
+cvar_t sv_gameplayfix_downtracesupportsongroundflag = {CF_SERVER, "sv_gameplayfix_downtracesupportsongroundflag", "1", "prevents very short moves from clearing onground (which may make the player stick to the floor at high netfps), fixes groundentity not being set when walking onto a mover with sv_gameplayfix_nogravityonground"};
 cvar_t sv_gameplayfix_q1bsptracelinereportstexture = {CF_SERVER, "sv_gameplayfix_q1bsptracelinereportstexture", "1", "enables mods to get accurate trace_texture results on q1bsp by using a surface-hitting traceline implementation rather than the standard solidbsp method, q3bsp always reports texture accurately"};
-cvar_t sv_gameplayfix_unstickplayers = {CF_SERVER, "sv_gameplayfix_unstickplayers", "1", "big hack to try and fix the rare case of MOVETYPE_WALK entities getting stuck in the world clipping hull."};
-cvar_t sv_gameplayfix_unstickentities = {CF_SERVER, "sv_gameplayfix_unstickentities", "1", "hack to check if entities are crossing world collision hull and try to move them to the right position"};
+cvar_t sv_gameplayfix_unstickplayers = {CF_SERVER, "sv_gameplayfix_unstickplayers", "0", "big hack to try and fix the rare case of MOVETYPE_WALK entities getting stuck in the world clipping hull."};
+cvar_t sv_gameplayfix_unstickentities = {CF_SERVER, "sv_gameplayfix_unstickentities", "1", "hack to check if entities are crossing world collision hull and try to move them to the right position, superseded by sv_gameplayfix_nudgeoutofsolid"};
 cvar_t sv_gameplayfix_fixedcheckwatertransition = {CF_SERVER, "sv_gameplayfix_fixedcheckwatertransition", "1", "fix two very stupid bugs in SV_CheckWaterTransition when watertype is CONTENTS_EMPTY (the bugs causes waterlevel to be 1 on first frame, -1 on second frame - the fix makes it 0 on both frames)"};
-cvar_t sv_gameplayfix_customstats = {CF_SERVER, "sv_gameplayfix_customstats", "0", "Disable stats higher than 220, for use by certain games such as Xonotic"};
 cvar_t sv_gravity = {CF_SERVER | CF_NOTIFY, "sv_gravity","800", "how fast you fall (512 = roughly earth gravity)"};
 cvar_t sv_init_frame_count = {CF_SERVER, "sv_init_frame_count", "2", "number of frames to run to allow everything to settle before letting clients connect"};
 cvar_t sv_idealpitchscale = {CF_SERVER, "sv_idealpitchscale","0.8", "how much to look up/down slopes and stairs when not using freelook"};
 cvar_t sv_jumpstep = {CF_SERVER | CF_NOTIFY, "sv_jumpstep", "0", "whether you can step up while jumping"};
 cvar_t sv_jumpvelocity = {CF_SERVER, "sv_jumpvelocity", "270", "cvar that can be used by QuakeC code for jump velocity"};
+cvar_t sv_legacy_bbox_expand = {CF_SERVER, "sv_legacy_bbox_expand", "1", "before linking an entity to the area grid, decrease its mins and increase its maxs by '1 1 1', or '15 15 1' if it has flag FL_ITEM (this is the Quake/QuakeWorld behaviour); disable to make SVQC bboxes consistent with CSQC which never does this expansion"};
 cvar_t sv_maxairspeed = {CF_SERVER, "sv_maxairspeed", "30", "maximum speed a player can accelerate to when airborn (note that it is possible to completely stop by moving the opposite direction)"};
 cvar_t sv_maxrate = {CF_SERVER | CF_ARCHIVE | CF_NOTIFY, "sv_maxrate", "1000000", "upper limit on client rate cvar, should reflect your network connection quality"};
 cvar_t sv_maxspeed = {CF_SERVER | CF_NOTIFY, "sv_maxspeed", "320", "maximum speed a player can accelerate to when on ground (can be exceeded by tricks)"};
@@ -141,6 +143,7 @@ cvar_t sv_nostep = {CF_SERVER | CF_NOTIFY, "sv_nostep","0", "prevents MOVETYPE_S
 cvar_t sv_playerphysicsqc = {CF_SERVER | CF_NOTIFY, "sv_playerphysicsqc", "1", "enables QuakeC function to override player physics"};
 cvar_t sv_progs = {CF_SERVER, "sv_progs", "progs.dat", "selects which quakec progs.dat file to run" };
 cvar_t sv_protocolname = {CF_SERVER, "sv_protocolname", "DP7", "selects network protocol to host for (values include QUAKE, QUAKEDP, NEHAHRAMOVIE, DP1 and up)"};
+cvar_t sv_qcstats = {CF_SERVER, "sv_qcstats", "0", "Disables engine sending of stats 220 and above, for use by certain games such as Xonotic, NOTE: it's strongly recommended that SVQC send correct STAT_MOVEVARS_TICRATE and STAT_MOVEVARS_TIMESCALE"};
 cvar_t sv_random_seed = {CF_SERVER, "sv_random_seed", "", "random seed; when set, on every map start this random seed is used to initialize the random number generator. Don't touch it unless for benchmarking or debugging"};
 cvar_t host_limitlocal = {CF_SERVER, "host_limitlocal", "0", "whether to apply rate limiting to the local player in a listen server (only useful for testing)"};
 cvar_t sv_sound_land = {CF_SERVER, "sv_sound_land", "demon/dland2.wav", "sound to play when MOVETYPE_STEP entity hits the ground at high speed (empty cvar disables the sound)"};
@@ -157,10 +160,15 @@ cvar_t sv_warsowbunny_turnaccel = {CF_SERVER, "sv_warsowbunny_turnaccel", "0", "
 cvar_t sv_warsowbunny_backtosideratio = {CF_SERVER, "sv_warsowbunny_backtosideratio", "0.8", "lower values make it easier to change direction without losing speed; the drawback is \"understeering\" in sharp turns"};
 cvar_t sv_onlycsqcnetworking = {CF_SERVER, "sv_onlycsqcnetworking", "0", "disables legacy entity networking code for higher performance (except on clients, which can still be legacy)"};
 cvar_t sv_areadebug = {CF_SERVER, "sv_areadebug", "0", "disables physics culling for debugging purposes (only for development)"};
+
 cvar_t sys_ticrate = {CF_SERVER | CF_ARCHIVE, "sys_ticrate","0.0138889", "how long a server frame is in seconds, 0.05 is 20fps server rate, 0.1 is 10fps (can not be set higher than 0.1), 0 runs as many server frames as possible (makes games against bots a little smoother, overwhelms network players), 0.0138889 matches QuakeWorld physics"};
+cvar_t sv_maxphysicsframesperserverframe = {CF_SERVER, "sv_maxphysicsframesperserverframe","10", "maximum number of physics frames per server frame"};
+cvar_t sv_lagreporting_always = {CF_SERVER, "sv_lagreporting_always", "0", "report lag even in singleplayer, listen, or an empty dedicated server"};
+cvar_t sv_lagreporting_strict = {CF_SERVER, "sv_lagreporting_strict", "0", "log any extra frames run to catch up after a holdup (only applies when sv_maxphysicsframesperserverframe > 1)"};
+cvar_t sv_threaded = {CF_SERVER, "sv_threaded", "0", "enables a separate thread for server code, improving performance, especially when hosting a game while playing, EXPERIMENTAL, may be crashy"};
+
 cvar_t teamplay = {CF_SERVER | CF_NOTIFY, "teamplay","0", "teamplay mode, values depend on mod but typically 0 = no teams, 1 = no team damage no self damage, 2 = team damage and self damage, some mods support 3 = no team damage but can damage self"};
 cvar_t timelimit = {CF_SERVER | CF_NOTIFY, "timelimit","0", "ends level at this time (in minutes)"};
-cvar_t sv_threaded = {CF_SERVER, "sv_threaded", "0", "enables a separate thread for server code, improving performance, especially when hosting a game while playing, EXPERIMENTAL, may be crashy"};
 
 cvar_t sv_rollspeed = {CF_CLIENT, "sv_rollspeed", "200", "how much strafing is necessary to tilt the view"};
 cvar_t sv_rollangle = {CF_CLIENT, "sv_rollangle", "2.0", "how much to tilt the view when strafing"};
@@ -204,7 +212,7 @@ cvar_t sv_autodemo_perclient_discardable = {CF_SERVER | CF_ARCHIVE, "sv_autodemo
 
 cvar_t halflifebsp = {CF_SERVER, "halflifebsp", "0", "indicates the current map is hlbsp format (useful to know because of different bounding box sizes)"};
 cvar_t sv_mapformat_is_quake2 = {CF_SERVER, "sv_mapformat_is_quake2", "0", "indicates the current map is q2bsp format (useful to know because of different entity behaviors, .frame on submodels and other things)"};
-cvar_t sv_mapformat_is_quake3 = {CF_SERVER, "sv_mapformat_is_quake3", "0", "indicates the current map is q2bsp format (useful to know because of different entity behaviors)"};
+cvar_t sv_mapformat_is_quake3 = {CF_SERVER, "sv_mapformat_is_quake3", "0", "indicates the current map is q3bsp format (useful to know because of different entity behaviors)"};
 
 cvar_t sv_writepicture_quality = {CF_SERVER | CF_ARCHIVE, "sv_writepicture_quality", "10", "WritePicture quality offset (higher means better quality, but slower)"};
 
@@ -257,12 +265,11 @@ static const char *standardeffectnames[EFFECT_TOTAL] =
        "SVC_PARTICLE"
 };
 
-#define SV_REQFUNCS 0
-#define sv_reqfuncs NULL
 
-//#define SV_REQFUNCS (sizeof(sv_reqfuncs) / sizeof(const char *))
-//static const char *sv_reqfuncs[] = {
-//};
+static void SV_CheckRequiredFuncs(prvm_prog_t *prog, const char *filename)
+{
+       // no required funcs?!
+}
 
 #define SV_REQFIELDS (sizeof(sv_reqfields) / sizeof(prvm_required_field_t))
 
@@ -449,7 +456,6 @@ static void SV_ServerOptions (void)
        i = Sys_CheckParm ("-dedicated");
        if (i || !cl_available)
        {
-               cls.state = ca_dedicated;
                // check for -dedicated specifying how many players
                if (i && i + 1 < sys.argc && atoi (sys.argv[i+1]) >= 1)
                        svs.maxclients = atoi (sys.argv[i+1]);
@@ -549,6 +555,7 @@ void SV_Init (void)
        Cvar_RegisterVariable (&sv_allowdownloads_config);
        Cvar_RegisterVariable (&sv_allowdownloads_dlcache);
        Cvar_RegisterVariable (&sv_allowdownloads_inarchive);
+       Cvar_RegisterVariable (&sv_areagrid_link_SOLID_NOT);
        Cvar_RegisterVariable (&sv_areagrid_mingridsize);
        Cvar_RegisterVariable (&sv_checkforpacketsduringsleep);
        Cvar_RegisterVariable (&sv_clmovement_enable);
@@ -570,6 +577,7 @@ void SV_Init (void)
        Cvar_RegisterVariable (&sv_cullentities_trace_samples);
        Cvar_RegisterVariable (&sv_cullentities_trace_samples_extra);
        Cvar_RegisterVariable (&sv_cullentities_trace_samples_players);
+       Cvar_RegisterVariable (&sv_cullentities_trace_spectators);
        Cvar_RegisterVariable (&sv_debugmove);
        Cvar_RegisterVariable (&sv_echobprint);
        Cvar_RegisterVariable (&sv_edgefriction);
@@ -604,12 +612,13 @@ void SV_Init (void)
        Cvar_RegisterVariable (&sv_gameplayfix_unstickplayers);
        Cvar_RegisterVariable (&sv_gameplayfix_unstickentities);
        Cvar_RegisterVariable (&sv_gameplayfix_fixedcheckwatertransition);
-       Cvar_RegisterVariable (&sv_gameplayfix_customstats);
+       Cvar_RegisterVariable (&sv_qcstats);
        Cvar_RegisterVariable (&sv_gravity);
        Cvar_RegisterVariable (&sv_init_frame_count);
        Cvar_RegisterVariable (&sv_idealpitchscale);
        Cvar_RegisterVariable (&sv_jumpstep);
        Cvar_RegisterVariable (&sv_jumpvelocity);
+       Cvar_RegisterVariable (&sv_legacy_bbox_expand);
        Cvar_RegisterVariable (&sv_maxairspeed);
        Cvar_RegisterVariable (&sv_maxrate);
        Cvar_RegisterVariable (&sv_maxspeed);
@@ -635,10 +644,15 @@ void SV_Init (void)
        Cvar_RegisterVariable (&sv_warsowbunny_backtosideratio);
        Cvar_RegisterVariable (&sv_onlycsqcnetworking);
        Cvar_RegisterVariable (&sv_areadebug);
+
        Cvar_RegisterVariable (&sys_ticrate);
+       Cvar_RegisterVariable (&sv_maxphysicsframesperserverframe);
+       Cvar_RegisterVariable (&sv_lagreporting_always);
+       Cvar_RegisterVariable (&sv_lagreporting_strict);
+       Cvar_RegisterVariable (&sv_threaded);
+
        Cvar_RegisterVariable (&teamplay);
        Cvar_RegisterVariable (&timelimit);
-       Cvar_RegisterVariable (&sv_threaded);
 
        Cvar_RegisterVariable (&sv_rollangle);
        Cvar_RegisterVariable (&sv_rollspeed);
@@ -781,7 +795,7 @@ void SV_SendServerinfo (client_t *client)
 
        SZ_Clear (&client->netconnection->message);
        MSG_WriteByte (&client->netconnection->message, svc_print);
-       dpsnprintf (message, sizeof (message), "\nServer: %s build %s (progs %i crc)\n", gamename, buildstring, prog->filecrc);
+       dpsnprintf (message, sizeof (message), "\nServer: %s (progs %i crc)\n", engineversion, prog->filecrc);
        MSG_WriteString (&client->netconnection->message,message);
 
        SV_StopDemoRecording(client); // to split up demos into different files
@@ -944,8 +958,8 @@ void SV_ConnectClient (int clientnum, netconn_t *netconnection)
                                );
        }
 
-       strlcpy(client->name, "unconnected", sizeof(client->name));
-       strlcpy(client->old_name, "unconnected", sizeof(client->old_name));
+       dp_strlcpy(client->name, "unconnected", sizeof(client->name));
+       dp_strlcpy(client->old_name, "unconnected", sizeof(client->old_name));
        client->prespawned = false;
        client->spawned = false;
        client->begun = false;
@@ -1222,7 +1236,7 @@ static void SV_Download_f(cmd_state_t *cmd)
 
        Download_CheckExtensions(cmd);
 
-       strlcpy(host_client->download_name, Cmd_Argv(cmd, 1), sizeof(host_client->download_name));
+       dp_strlcpy(host_client->download_name, Cmd_Argv(cmd, 1), sizeof(host_client->download_name));
        extension = FS_FileExtension(host_client->download_name);
 
        // host_client is asking to download a specified file
@@ -1235,7 +1249,7 @@ static void SV_Download_f(cmd_state_t *cmd)
                extensions[0] = '\0';
                
                if(host_client->download_deflate)
-                       strlcat(extensions, " deflate", sizeof(extensions));
+                       dp_strlcat(extensions, " deflate", sizeof(extensions));
                
                Con_DPrintf("Downloading %s to %s\n", host_client->download_name, host_client->name);
 
@@ -1390,7 +1404,7 @@ int SV_ModelIndex(const char *s, int precachemode)
        // testing
        //if (precachemode == 2)
        //      return 0;
-       strlcpy(filename, s, sizeof(filename));
+       dp_strlcpy(filename, s, sizeof(filename));
        for (i = 2;i < limit;i++)
        {
                if (!sv.model_precache[i][0])
@@ -1404,7 +1418,7 @@ int SV_ModelIndex(const char *s, int precachemode)
                                }
                                if (precachemode == 1)
                                        Con_Printf("SV_ModelIndex(\"%s\"): not precached (fix your code), precaching anyway\n", filename);
-                               strlcpy(sv.model_precache[i], filename, sizeof(sv.model_precache[i]));
+                               dp_strlcpy(sv.model_precache[i], filename, sizeof(sv.model_precache[i]));
                                if (sv.state == ss_loading)
                                {
                                        // running from SV_SpawnServer which is launched from the client console command interpreter
@@ -1453,7 +1467,7 @@ int SV_SoundIndex(const char *s, int precachemode)
        // testing
        //if (precachemode == 2)
        //      return 0;
-       strlcpy(filename, s, sizeof(filename));
+       dp_strlcpy(filename, s, sizeof(filename));
        for (i = 1;i < limit;i++)
        {
                if (!sv.sound_precache[i][0])
@@ -1467,7 +1481,7 @@ int SV_SoundIndex(const char *s, int precachemode)
                                }
                                if (precachemode == 1)
                                        Con_Printf("SV_SoundIndex(\"%s\"): not precached (fix your code), precaching anyway\n", filename);
-                               strlcpy(sv.sound_precache[i], filename, sizeof(sv.sound_precache[i]));
+                               dp_strlcpy(sv.sound_precache[i], filename, sizeof(sv.sound_precache[i]));
                                if (sv.state != ss_loading)
                                {
                                        MSG_WriteByte(&sv.reliable_datagram, svc_precache);
@@ -1508,7 +1522,7 @@ int SV_ParticleEffectIndex(const char *name)
                sv.particleeffectnamesloaded = true;
                memset(sv.particleeffectname, 0, sizeof(sv.particleeffectname));
                for (i = 0;i < EFFECT_TOTAL;i++)
-                       strlcpy(sv.particleeffectname[i], standardeffectnames[i], sizeof(sv.particleeffectname[i]));
+                       dp_strlcpy(sv.particleeffectname[i], standardeffectnames[i], sizeof(sv.particleeffectname[i]));
                for (filepass = 0;;filepass++)
                {
                        if (filepass == 0)
@@ -1532,7 +1546,7 @@ int SV_ParticleEffectIndex(const char *name)
                                                break;
                                        if (argc < 16)
                                        {
-                                               strlcpy(argv[argc], com_token, sizeof(argv[argc]));
+                                               dp_strlcpy(argv[argc], com_token, sizeof(argv[argc]));
                                                argc++;
                                        }
                                }
@@ -1553,7 +1567,7 @@ int SV_ParticleEffectIndex(const char *name)
                                                        }
                                                        else
                                                        {
-                                                               strlcpy(sv.particleeffectname[effectnameindex], argv[1], sizeof(sv.particleeffectname[effectnameindex]));
+                                                               dp_strlcpy(sv.particleeffectname[effectnameindex], argv[1], sizeof(sv.particleeffectname[effectnameindex]));
                                                                break;
                                                        }
                                                }
@@ -1706,7 +1720,7 @@ static void SV_Prepare_CSQC(void)
 
                sv.csqc_progsize = (int)progsize;
                sv.csqc_progcrc = CRC_Block(svs.csqc_progdata, progsize);
-               strlcpy(sv.csqc_progname, csqc_progname.string, sizeof(sv.csqc_progname));
+               dp_strlcpy(sv.csqc_progname, csqc_progname.string, sizeof(sv.csqc_progname));
                Con_DPrintf("server detected csqc progs file \"%s\" with size %i and crc %i\n", sv.csqc_progname, sv.csqc_progsize, sv.csqc_progcrc);
 
                Con_DPrint("Compressing csprogs.dat\n");
@@ -1758,6 +1772,21 @@ int SV_IsLocalServer(void)
        return (host_isclient.integer && sv.active ? svs.maxclients : 0);
 }
 
+static void SV_VM_Shutdown(qbool prog_reset)
+{
+       prvm_prog_t *prog = SVVM_prog;
+
+       if(prog->loaded && PRVM_serverfunction(SV_Shutdown))
+       {
+               func_t s = PRVM_serverfunction(SV_Shutdown);
+               PRVM_serverglobalfloat(time) = sv.time;
+               PRVM_serverfunction(SV_Shutdown) = 0; // prevent it from getting called again
+               prog->ExecuteProgram(prog, s,"SV_Shutdown() required");
+       }
+       if (prog_reset)
+               PRVM_Prog_Reset(prog);
+}
+
 /*
 ================
 SV_SpawnServer
@@ -1774,21 +1803,26 @@ void SV_SpawnServer (const char *map)
        char *entities;
        model_t *worldmodel;
        char modelname[sizeof(sv.worldname)];
+       const char *canonicalname;
        char vabuf[1024];
 
        Con_Printf("SpawnServer: %s\n", map);
 
        dpsnprintf (modelname, sizeof(modelname), "maps/%s.bsp", map);
 
-       if (!FS_FileExists(modelname))
+       if (!(canonicalname = FS_FileExists(modelname)))
        {
                dpsnprintf (modelname, sizeof(modelname), "maps/%s", map);
-               if (!FS_FileExists(modelname))
+               if (!(canonicalname = FS_FileExists(modelname)))
                {
-                       Con_Printf("SpawnServer: no map file named %s\n", modelname);
+                       Con_Printf(CON_ERROR "SpawnServer: no map file named %s.bsp\n", modelname);
                        return;
                }
        }
+       // if it's not in a pak canonicalname will be the same pointer as modelname
+       // if it's in a pak canonicalname may differ by case
+       if (modelname != canonicalname)
+               dp_strlcpy(modelname, canonicalname, sizeof(modelname));
 
 //     SV_LockThreadMutex();
 
@@ -1801,16 +1835,7 @@ void SV_SpawnServer (const char *map)
        }
 
        if(sv.active)
-       {
-               World_End(&sv.world);
-               if(PRVM_serverfunction(SV_Shutdown))
-               {
-                       func_t s = PRVM_serverfunction(SV_Shutdown);
-                       PRVM_serverglobalfloat(time) = sv.time;
-                       PRVM_serverfunction(SV_Shutdown) = 0; // prevent it from getting called again
-                       prog->ExecuteProgram(prog, s,"SV_Shutdown() required");
-               }
-       }
+               SV_VM_Shutdown(false);
 
        // free q3 shaders so that any newly downloaded shaders will be active
        Mod_FreeQ3Shaders();
@@ -1818,7 +1843,7 @@ void SV_SpawnServer (const char *map)
        worldmodel = Mod_ForName(modelname, false, developer.integer > 0, NULL);
        if (!worldmodel || !worldmodel->TraceBox)
        {
-               Con_Printf("Couldn't load map %s\n", modelname);
+               Con_Printf(CON_ERROR "Couldn't load map %s\n", modelname);
 
                if(!host_isclient.integer)
                        Sys_MakeProcessMean();
@@ -1884,6 +1909,10 @@ void SV_SpawnServer (const char *map)
 // set up the new server
 //
        memset (&sv, 0, sizeof(sv));
+
+       // tell SV_Frame() to reset its timers
+       sv.spawnframe = host.framecount;
+
        // if running a local client, make sure it doesn't try to access the last
        // level's data which is no longer valiud
        cls.signon = 0;
@@ -1903,10 +1932,10 @@ void SV_SpawnServer (const char *map)
        sv.active = true;
 
        // set level base name variables for later use
-       strlcpy (sv.name, map, sizeof (sv.name));
-       strlcpy(sv.worldname, modelname, sizeof(sv.worldname));
+       dp_strlcpy(sv.worldname, modelname, sizeof(sv.worldname));
        FS_StripExtension(sv.worldname, sv.worldnamenoextension, sizeof(sv.worldnamenoextension));
-       strlcpy(sv.worldbasename, !strncmp(sv.worldnamenoextension, "maps/", 5) ? sv.worldnamenoextension + 5 : sv.worldnamenoextension, sizeof(sv.worldbasename));
+       dp_strlcpy(sv.worldbasename, !strncasecmp(sv.worldnamenoextension, "maps/", 5) ? sv.worldnamenoextension + 5 : sv.worldnamenoextension, sizeof(sv.worldbasename));
+//     dp_strlcpy(sv.name, sv.worldbasename, sizeof (sv.name)); // TODO can we just remove this now?
        //Cvar_SetQuick(&sv_worldmessage, sv.worldmessage); // set later after QC is spawned
        Cvar_SetQuick(&sv_worldname, sv.worldname);
        Cvar_SetQuick(&sv_worldnamenoextension, sv.worldnamenoextension);
@@ -1957,17 +1986,17 @@ void SV_SpawnServer (const char *map)
        World_SetSize(&sv.world, sv.worldname, sv.worldmodel->normalmins, sv.worldmodel->normalmaxs, prog);
        World_Start(&sv.world);
 
-       strlcpy(sv.sound_precache[0], "", sizeof(sv.sound_precache[0]));
+       dp_strlcpy(sv.sound_precache[0], "", sizeof(sv.sound_precache[0]));
 
-       strlcpy(sv.model_precache[0], "", sizeof(sv.model_precache[0]));
-       strlcpy(sv.model_precache[1], sv.worldname, sizeof(sv.model_precache[1]));
+       dp_strlcpy(sv.model_precache[0], "", sizeof(sv.model_precache[0]));
+       dp_strlcpy(sv.model_precache[1], sv.worldname, sizeof(sv.model_precache[1]));
        for (i = 1;i < sv.worldmodel->brush.numsubmodels && i+1 < MAX_MODELS;i++)
        {
                dpsnprintf(sv.model_precache[i+1], sizeof(sv.model_precache[i+1]), "*%i", i);
                sv.models[i+1] = Mod_ForName (sv.model_precache[i+1], false, false, sv.worldname);
        }
        if(i < sv.worldmodel->brush.numsubmodels)
-               Con_Printf("Too many submodels (MAX_MODELS is %i)\n", MAX_MODELS);
+               Con_Printf(CON_WARN "Too many submodels (MAX_MODELS is %i)\n", MAX_MODELS);
 
 //
 // load the rest of the entities
@@ -1990,7 +2019,7 @@ void SV_SpawnServer (const char *map)
        else
                PRVM_serverglobalfloat(deathmatch) = deathmatch.integer;
 
-       PRVM_serverglobalstring(mapname) = PRVM_SetEngineString(prog, sv.name);
+       PRVM_serverglobalstring(mapname) = PRVM_SetEngineString(prog, sv.worldbasename);
 
 // serverflags are for cross level information (sigils)
        PRVM_serverglobalfloat(serverflags) = svs.serverflags;
@@ -2072,9 +2101,8 @@ void SV_SpawnServer (const char *map)
                }
        }
 
-       // update the map title cvar
-       strlcpy(sv.worldmessage, PRVM_GetString(prog, PRVM_serveredictstring(prog->edicts, message)), sizeof(sv.worldmessage)); // map title (not related to filename)
-       Cvar_SetQuick(&sv_worldmessage, sv.worldmessage);
+       // update the map title cvar (not related to filename)
+       Cvar_SetQuick(&sv_worldmessage, PRVM_GetString(prog, PRVM_serveredictstring(prog->edicts, message)));
 
        Con_Printf("Server spawned.\n");
        NetConn_Heartbeat (2);
@@ -2094,7 +2122,6 @@ This only happens at the end of a game, not between levels
 */
 void SV_Shutdown(void)
 {
-       prvm_prog_t *prog = SVVM_prog;
        int i;
 
        SV_LockThreadMutex();
@@ -2107,22 +2134,13 @@ void SV_Shutdown(void)
        NetConn_Heartbeat(2);
        NetConn_Heartbeat(2);
 
-// make sure all the clients know we're disconnecting
-       World_End(&sv.world);
-       if(prog->loaded)
-       {
-               if(PRVM_serverfunction(SV_Shutdown))
-               {
-                       func_t s = PRVM_serverfunction(SV_Shutdown);
-                       PRVM_serverglobalfloat(time) = sv.time;
-                       PRVM_serverfunction(SV_Shutdown) = 0; // prevent it from getting called again
-                       prog->ExecuteProgram(prog, s,"SV_Shutdown() required");
-               }
-       }
+       // make sure all the clients know we're disconnecting
        for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
                if (host_client->active)
                        SV_DropClient(false, "Server shutting down"); // server shutdown
 
+       SV_VM_Shutdown(true);
+
        NetConn_CloseServerPorts();
 
        sv.active = false;
@@ -2151,7 +2169,7 @@ static void SVVM_end_increase_edicts(prvm_prog_t *prog)
 
        // link every entity except world
        for (i = 1, ent = prog->edicts;i < prog->num_edicts;i++, ent++)
-               if (!ent->free && !VectorCompare(PRVM_serveredictvector(ent, absmin), PRVM_serveredictvector(ent, absmax)))
+               if (!ent->free)
                        SV_LinkEdict(ent);
 }
 
@@ -2327,7 +2345,7 @@ static void SV_VM_Setup(void)
        prog->error_cmd             = Host_Error;
        prog->ExecuteProgram        = SVVM_ExecuteProgram;
 
-       PRVM_Prog_Load(prog, sv_progs.string, NULL, 0, SV_REQFUNCS, sv_reqfuncs, SV_REQFIELDS, sv_reqfields, SV_REQGLOBALS, sv_reqglobals);
+       PRVM_Prog_Load(prog, sv_progs.string, NULL, 0, SV_CheckRequiredFuncs, SV_REQFIELDS, sv_reqfields, SV_REQGLOBALS, sv_reqglobals);
 
        // some mods compiled with scrambling compilers lack certain critical
        // global names and field names such as "self" and "time" and "nextthink"
@@ -2497,55 +2515,61 @@ Returns a time report string, for example for
 */
 const char *SV_TimingReport(char *buf, size_t buflen)
 {
-       return va(buf, buflen, "%.1f%% CPU, %.2f%% lost, offset avg %.1fms, max %.1fms, sdev %.1fms", svs.perf_cpuload * 100, svs.perf_lost * 100, svs.perf_offset_avg * 1000, svs.perf_offset_max * 1000, svs.perf_offset_sdev * 1000);
+       return va(buf, buflen, "%.1f%% CPU, %.2f%% lost, offset avg %.1fms, max %.1fms, sdev %.1fms", sv.perf_cpuload * 100, sv.perf_lost * 100, sv.perf_offset_avg * 1000, sv.perf_offset_max * 1000, sv.perf_offset_sdev * 1000);
 }
 
-extern cvar_t host_maxwait;
 extern cvar_t host_framerate;
-extern cvar_t cl_maxphysicsframesperserverframe;
 double SV_Frame(double time)
 {
        static double sv_timer;
        int i;
        char vabuf[1024];
-       qbool playing = false;
+       qbool reporting = false;
+
+       // reset timer after level change
+       if (host.framecount == sv.spawnframe || host.framecount == sv.spawnframe + 1)
+               sv_timer = time = host.sleeptime = 0;
 
        if (!svs.threaded)
        {
-               svs.perf_acc_sleeptime = host.sleeptime;
-               svs.perf_acc_realtime += time;
+               sv.perf_acc_sleeptime += host.sleeptime;
+               sv.perf_acc_realtime += time;
 
-               // Look for clients who have spawned
-               for (i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++)
-                       if(host_client->begun && host_client->netconnection)
-                               playing = true;
+               if (sv_lagreporting_always.integer)
+                       reporting = true;
+               else if (cls.state == ca_dedicated)
+               {
+                       // Report lag if there's players, so they know it wasn't the network or their machine
+                       for (i = 0; i < svs.maxclients; ++i)
+                       {
+                               if (svs.clients[i].begun && svs.clients[i].netconnection)
+                               {
+                                       reporting = true;
+                                       break;
+                               }
+                       }
+               }
 
-               if(svs.perf_acc_realtime > 5)
+               if(sv.perf_acc_realtime > 5)
                {
-                       svs.perf_cpuload = 1 - svs.perf_acc_sleeptime / svs.perf_acc_realtime;
-                       svs.perf_lost = svs.perf_acc_lost / svs.perf_acc_realtime;
+                       sv.perf_cpuload = 1 - sv.perf_acc_sleeptime / sv.perf_acc_realtime;
+                       sv.perf_lost = sv.perf_acc_lost / sv.perf_acc_realtime;
 
-                       if(svs.perf_acc_offset_samples > 0)
+                       if(sv.perf_acc_offset_samples > 0)
                        {
-                               svs.perf_offset_max = svs.perf_acc_offset_max;
-                               svs.perf_offset_avg = svs.perf_acc_offset / svs.perf_acc_offset_samples;
-                               svs.perf_offset_sdev = sqrt(svs.perf_acc_offset_squared / svs.perf_acc_offset_samples - svs.perf_offset_avg * svs.perf_offset_avg);
+                               sv.perf_offset_max = sv.perf_acc_offset_max;
+                               sv.perf_offset_avg = sv.perf_acc_offset / sv.perf_acc_offset_samples;
+                               sv.perf_offset_sdev = sv.perf_acc_offset_squared / sv.perf_acc_offset_samples - sv.perf_offset_avg * sv.perf_offset_avg;
+                               sv.perf_offset_sdev = sv.perf_offset_sdev > 0 ? sqrt(sv.perf_offset_sdev) : 0;
                        }
 
-                       if(svs.perf_lost > 0 && developer_extra.integer && playing) // only complain if anyone is looking
-                               Con_DPrintf("Server can't keep up: %s\n", SV_TimingReport(vabuf, sizeof(vabuf)));
-               }
+                       if (sv.perf_lost > 0 && reporting)
+                               SV_BroadcastPrintf("\003" CON_WARN "Server lag report: %s\n", SV_TimingReport(vabuf, sizeof(vabuf)));
 
-               if(svs.perf_acc_realtime > 5 || sv.time < 10)
-               {
-                       /*
-                        * Don't accumulate time for the first 10 seconds of a match
-                        * so things can settle
-                        */
-                       svs.perf_acc_realtime = svs.perf_acc_sleeptime =
-                       svs.perf_acc_lost = svs.perf_acc_offset =
-                       svs.perf_acc_offset_squared = svs.perf_acc_offset_max =
-                       svs.perf_acc_offset_samples = host.sleeptime = 0;
+                       sv.perf_acc_realtime = sv.perf_acc_sleeptime =
+                       sv.perf_acc_lost = sv.perf_acc_offset =
+                       sv.perf_acc_offset_squared = sv.perf_acc_offset_max =
+                       sv.perf_acc_offset_samples = 0;
                }
 
                /*
@@ -2571,7 +2595,7 @@ double SV_Frame(double time)
        if (sv_timer > 0.1)
        {
                if (!svs.threaded)
-                       svs.perf_acc_lost += (sv_timer - 0.1);
+                       sv.perf_acc_lost += (sv_timer - 0.1);
                sv_timer = 0.1;
        }
 
@@ -2598,7 +2622,8 @@ double SV_Frame(double time)
                {
                        advancetime = sys_ticrate.value;
                        // listen servers can run multiple server frames per client frame
-                       framelimit = cl_maxphysicsframesperserverframe.integer;
+                       if (sv_maxphysicsframesperserverframe.integer > 0)
+                               framelimit = sv_maxphysicsframesperserverframe.integer;
                        aborttime = Sys_DirtyTime() + 0.1;
                }
 
@@ -2614,12 +2639,12 @@ double SV_Frame(double time)
                                offset = 0;
 
                        offset += sv_timer;
-                       ++svs.perf_acc_offset_samples;
-                       svs.perf_acc_offset += offset;
-                       svs.perf_acc_offset_squared += offset * offset;
+                       ++sv.perf_acc_offset_samples;
+                       sv.perf_acc_offset += offset;
+                       sv.perf_acc_offset_squared += offset * offset;
                        
-                       if(svs.perf_acc_offset_max < offset)
-                               svs.perf_acc_offset_max = offset;
+                       if(sv.perf_acc_offset_max < offset)
+                               sv.perf_acc_offset_max = offset;
                }
 
                // only advance time if not paused
@@ -2643,6 +2668,9 @@ double SV_Frame(double time)
                                break;
                }
 
+               if (framecount > 1 && sv_lagreporting_strict.integer && reporting)
+                       SV_BroadcastPrintf(CON_WARN "Server lag report: caught up %.1fms by running %d extra frames\n", advancetime * (framecount - 1) * 1000, framecount - 1);
+
                R_TimeReport("serverphysics");
 
                // send all messages to the clients
@@ -2669,7 +2697,7 @@ double SV_Frame(double time)
        if (sv_timer >= 0)
        {
                if (!svs.threaded)
-                       svs.perf_acc_lost += sv_timer;
+                       sv.perf_acc_lost += sv_timer;
                sv_timer = 0;
        }
 
@@ -2682,7 +2710,6 @@ static int SV_ThreadFunc(void *voiddata)
        qbool playing = false;
        double sv_timer = 0;
        double sv_deltarealtime, sv_oldrealtime, sv_realtime;
-       double wait;
        int i;
        char vabuf[1024];
        sv_realtime = Sys_DirtyTime();
@@ -2699,7 +2726,7 @@ static int SV_ThreadFunc(void *voiddata)
 
                sv_timer += sv_deltarealtime;
 
-               svs.perf_acc_realtime += sv_deltarealtime;
+               sv.perf_acc_realtime += sv_deltarealtime;
 
                // at this point we start doing real server work, and must block on any client activity pertaining to the server (such as executing SV_SpawnServer)
                SV_LockThreadMutex();
@@ -2715,22 +2742,22 @@ static int SV_ThreadFunc(void *voiddata)
                {
                        // don't accumulate time for the first 10 seconds of a match
                        // so things can settle
-                       svs.perf_acc_realtime = svs.perf_acc_sleeptime = svs.perf_acc_lost = svs.perf_acc_offset = svs.perf_acc_offset_squared = svs.perf_acc_offset_max = svs.perf_acc_offset_samples = 0;
+                       sv.perf_acc_realtime = sv.perf_acc_sleeptime = sv.perf_acc_lost = sv.perf_acc_offset = sv.perf_acc_offset_squared = sv.perf_acc_offset_max = sv.perf_acc_offset_samples = 0;
                }
-               else if(svs.perf_acc_realtime > 5)
+               else if(sv.perf_acc_realtime > 5)
                {
-                       svs.perf_cpuload = 1 - svs.perf_acc_sleeptime / svs.perf_acc_realtime;
-                       svs.perf_lost = svs.perf_acc_lost / svs.perf_acc_realtime;
-                       if(svs.perf_acc_offset_samples > 0)
+                       sv.perf_cpuload = 1 - sv.perf_acc_sleeptime / sv.perf_acc_realtime;
+                       sv.perf_lost = sv.perf_acc_lost / sv.perf_acc_realtime;
+                       if(sv.perf_acc_offset_samples > 0)
                        {
-                               svs.perf_offset_max = svs.perf_acc_offset_max;
-                               svs.perf_offset_avg = svs.perf_acc_offset / svs.perf_acc_offset_samples;
-                               svs.perf_offset_sdev = sqrt(svs.perf_acc_offset_squared / svs.perf_acc_offset_samples - svs.perf_offset_avg * svs.perf_offset_avg);
+                               sv.perf_offset_max = sv.perf_acc_offset_max;
+                               sv.perf_offset_avg = sv.perf_acc_offset / sv.perf_acc_offset_samples;
+                               sv.perf_offset_sdev = sqrt(sv.perf_acc_offset_squared / sv.perf_acc_offset_samples - sv.perf_offset_avg * sv.perf_offset_avg);
                        }
-                       if(svs.perf_lost > 0 && developer_extra.integer)
+                       if(sv.perf_lost > 0 && developer_extra.integer)
                                if(playing)
                                        Con_DPrintf("Server can't keep up: %s\n", SV_TimingReport(vabuf, sizeof(vabuf)));
-                       svs.perf_acc_realtime = svs.perf_acc_sleeptime = svs.perf_acc_lost = svs.perf_acc_offset = svs.perf_acc_offset_squared = svs.perf_acc_offset_max = svs.perf_acc_offset_samples = 0;
+                       sv.perf_acc_realtime = sv.perf_acc_sleeptime = sv.perf_acc_lost = sv.perf_acc_offset = sv.perf_acc_offset_squared = sv.perf_acc_offset_max = sv.perf_acc_offset_samples = 0;
                }
 
                // get new packets
@@ -2741,21 +2768,10 @@ static int SV_ThreadFunc(void *voiddata)
                }
 
                // if the accumulators haven't become positive yet, wait a while
-               wait = sv_timer * -1000000.0;
-               if (wait >= 1)
+               if (sv_timer < 0)
                {
-                       double time0, delta;
                        SV_UnlockThreadMutex(); // don't keep mutex locked while sleeping
-                       if (host_maxwait.value <= 0)
-                               wait = min(wait, 1000000.0);
-                       else
-                               wait = min(wait, host_maxwait.value * 1000.0);
-                       if(wait < 1)
-                               wait = 1; // because we cast to int
-                       time0 = Sys_DirtyTime();
-                       Sys_Sleep((int)wait);
-                       delta = Sys_DirtyTime() - time0;if (delta < 0 || delta >= 1800) delta = 0;
-                       svs.perf_acc_sleeptime += delta;
+                       sv.perf_acc_sleeptime += Sys_Sleep(-sv_timer);
                        continue;
                }
 
@@ -2773,11 +2789,11 @@ static int SV_ThreadFunc(void *voiddata)
                        if(advancetime > 0)
                        {
                                offset = sv_timer + (Sys_DirtyTime() - sv_realtime); // LadyHavoc: FIXME: I don't understand this line
-                               ++svs.perf_acc_offset_samples;
-                               svs.perf_acc_offset += offset;
-                               svs.perf_acc_offset_squared += offset * offset;
-                               if(svs.perf_acc_offset_max < offset)
-                                       svs.perf_acc_offset_max = offset;
+                               ++sv.perf_acc_offset_samples;
+                               sv.perf_acc_offset += offset;
+                               sv.perf_acc_offset_squared += offset * offset;
+                               if(sv.perf_acc_offset_max < offset)
+                                       sv.perf_acc_offset_max = offset;
                        }
 
                        // only advance time if not paused
@@ -2815,7 +2831,7 @@ static int SV_ThreadFunc(void *voiddata)
                // if there is some time remaining from this frame, reset the timers
                if (sv_timer >= 0)
                {
-                       svs.perf_acc_lost += sv_timer;
+                       sv.perf_acc_lost += sv_timer;
                        sv_timer = 0;
                }
        }
index a918ea25603c7145aa59e09174643011a5e22152..411a7c969b748382a723df0a2ef2d28983d17d63 100644 (file)
--- a/sv_phys.c
+++ b/sv_phys.c
@@ -409,7 +409,7 @@ SV_Move
 ==================
 */
 #if COLLISIONPARANOID >= 1
-trace_t SV_TraceBox_(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask, int skipsupercontentsmask, int skipmaterialflagsmask, float extend)
+static trace_t SV_TraceBox_(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask, int skipsupercontentsmask, int skipmaterialflagsmask, float extend)
 #else
 trace_t SV_TraceBox(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask, int skipsupercontentsmask, int skipmaterialflagsmask, float extend)
 #endif
@@ -588,17 +588,17 @@ finished:
 }
 
 #if COLLISIONPARANOID >= 1
-trace_t SV_TraceBox(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask, int skipsupercontentsmask, int skipmaterialflagsmask)
+trace_t SV_TraceBox(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask, int skipsupercontentsmask, int skipmaterialflagsmask, float extend)
 {
        prvm_prog_t *prog = SVVM_prog;
        int endstuck;
        trace_t trace;
        vec3_t temp;
-       trace = SV_TraceBox_(start, mins, maxs, end, type, passedict, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
+       trace = SV_TraceBox_(start, mins, maxs, end, type, passedict, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend);
        if (passedict)
        {
                VectorCopy(trace.endpos, temp);
-               endstuck = SV_TraceBox_(temp, mins, maxs, temp, type, passedict, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask).startsolid;
+               endstuck = SV_TraceBox_(temp, mins, maxs, temp, type, passedict, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend).startsolid;
 #if COLLISIONPARANOID < 3
                if (trace.startsolid || endstuck)
 #endif
@@ -875,35 +875,36 @@ void SV_LinkEdict (prvm_edict_t *ent)
                VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, maxs), maxs);
        }
 
-//
-// to make items easier to pick up and allow them to be grabbed off
-// of shelves, the abs sizes are expanded
-//
-       if ((int)PRVM_serveredictfloat(ent, flags) & FL_ITEM)
+       if (sv_legacy_bbox_expand.integer)
        {
-               mins[0] -= 15;
-               mins[1] -= 15;
-               mins[2] -= 1;
-               maxs[0] += 15;
-               maxs[1] += 15;
-               maxs[2] += 1;
-       }
-       else
-       {
-               // because movement is clipped an epsilon away from an actual edge,
-               // we must fully check even when bounding boxes don't quite touch
-               mins[0] -= 1;
-               mins[1] -= 1;
-               mins[2] -= 1;
-               maxs[0] += 1;
-               maxs[1] += 1;
-               maxs[2] += 1;
+               if ((int)PRVM_serveredictfloat(ent, flags) & FL_ITEM)
+               {
+                       // to make items easier to pick up and allow them to be grabbed off
+                       // of shelves, the abs sizes are expanded
+                       mins[0] -= 15;
+                       mins[1] -= 15;
+                       mins[2] -= 1;
+                       maxs[0] += 15;
+                       maxs[1] += 15;
+                       maxs[2] += 1;
+               }
+               else
+               {
+                       // because movement is clipped an epsilon away from an actual edge,
+                       // we must fully check even when bounding boxes don't quite touch
+                       mins[0] -= 1;
+                       mins[1] -= 1;
+                       mins[2] -= 1;
+                       maxs[0] += 1;
+                       maxs[1] += 1;
+                       maxs[2] += 1;
+               }
        }
 
        VectorCopy(mins, PRVM_serveredictvector(ent, absmin));
        VectorCopy(maxs, PRVM_serveredictvector(ent, absmax));
 
-       World_LinkEdict(&sv.world, ent, mins, maxs);
+       World_LinkEdict(&sv.world, ent, mins, maxs, sv_areagrid_link_SOLID_NOT.integer);
 }
 
 /*
@@ -1040,12 +1041,12 @@ void SV_CheckVelocity (prvm_edict_t *ent)
 //
        for (i=0 ; i<3 ; i++)
        {
-               if (PRVM_IS_NAN(PRVM_serveredictvector(ent, velocity)[i]))
+               if (isnan(PRVM_serveredictvector(ent, velocity)[i]))
                {
                        Con_Printf("Got a NaN velocity on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
                        PRVM_serveredictvector(ent, velocity)[i] = 0;
                }
-               if (PRVM_IS_NAN(PRVM_serveredictvector(ent, origin)[i]))
+               if (isnan(PRVM_serveredictvector(ent, origin)[i]))
                {
                        Con_Printf("Got a NaN origin on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
                        PRVM_serveredictvector(ent, origin)[i] = 0;
@@ -1193,13 +1194,12 @@ If stepnormal is not NULL, the plane normal of any vertical wall hit will be sto
 ============
 */
 static float SV_Gravity (prvm_edict_t *ent);
-static qbool SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qbool dolink);
+static qbool SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qbool dolink, qbool checkstuck);
 #define MAX_CLIP_PLANES 5
 static int SV_FlyMove (prvm_edict_t *ent, float time, qbool applygravity, float *stepnormal, int hitsupercontentsmask, int skipsupercontentsmask, int skipmaterialflagsmask, float stepheight)
 {
        prvm_prog_t *prog = SVVM_prog;
-       int blocked, bumpcount;
-       int i, j, numplanes;
+       unsigned int i, j, numplanes, blocked, bumpcount;
        float d, time_left, gravity;
        vec3_t dir, push, planes[MAX_CLIP_PLANES];
        prvm_vec3_t primal_velocity, original_velocity, new_velocity, restore_velocity;
@@ -1237,7 +1237,7 @@ static int SV_FlyMove (prvm_edict_t *ent, float time, qbool applygravity, float
                        break;
 
                VectorScale(PRVM_serveredictvector(ent, velocity), time_left, push);
-               if(!SV_PushEntity(&trace, ent, push, false))
+               if(!SV_PushEntity(&trace, ent, push, false, true))
                {
                        // we got teleported by a touch function
                        // let's abort the move
@@ -1247,7 +1247,7 @@ static int SV_FlyMove (prvm_edict_t *ent, float time, qbool applygravity, float
 
                // this code is used by MOVETYPE_WALK and MOVETYPE_STEP and SV_UnstickEntity
                // abort move if we're stuck in the world (and didn't make it out)
-               if (trace.worldstartsolid && trace.allsolid)
+               if (trace.worldstartsolid && trace.allsolid && trace.startdepth < 0)
                {
                        VectorCopy(restore_velocity, PRVM_serveredictvector(ent, velocity));
                        return 3;
@@ -1255,6 +1255,9 @@ static int SV_FlyMove (prvm_edict_t *ent, float time, qbool applygravity, float
 
                if (trace.fraction == 1)
                        break;
+
+               time_left *= 1 - trace.fraction;
+
                if (trace.plane.normal[2])
                {
                        if (trace.plane.normal[2] > 0.7)
@@ -1282,28 +1285,30 @@ static int SV_FlyMove (prvm_edict_t *ent, float time, qbool applygravity, float
                        trace_t steptrace3;
                        //Con_Printf("step %f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
                        VectorSet(steppush, 0, 0, stepheight);
+                       VectorScale(PRVM_serveredictvector(ent, velocity), time_left, push);
                        VectorCopy(PRVM_serveredictvector(ent, origin), org);
-                       if(!SV_PushEntity(&steptrace, ent, steppush, false))
+                       if(!SV_PushEntity(&steptrace, ent, steppush, false, true))
                        {
                                blocked |= 8;
                                break;
                        }
                        //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
-                       if(!SV_PushEntity(&steptrace2, ent, push, false))
+                       if(!SV_PushEntity(&steptrace2, ent, push, false, true))
                        {
                                blocked |= 8;
                                break;
                        }
                        //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
                        VectorSet(steppush, 0, 0, org[2] - PRVM_serveredictvector(ent, origin)[2]);
-                       if(!SV_PushEntity(&steptrace3, ent, steppush, false))
+                       if(!SV_PushEntity(&steptrace3, ent, steppush, false, true))
                        {
                                blocked |= 8;
                                break;
                        }
                        //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
                        // accept the new position if it made some progress...
-                       if (fabs(PRVM_serveredictvector(ent, origin)[0] - org[0]) >= 0.03125 || fabs(PRVM_serveredictvector(ent, origin)[1] - org[1]) >= 0.03125)
+                       // previously this checked if absolute distance >= 0.03125 which made stepping up unreliable
+                       if (PRVM_serveredictvector(ent, origin)[0] - org[0] || PRVM_serveredictvector(ent, origin)[1] - org[1])
                        {
                                //Con_Printf("accepted (delta %f %f %f)\n", PRVM_serveredictvector(ent, origin)[0] - org[0], PRVM_serveredictvector(ent, origin)[1] - org[1], PRVM_serveredictvector(ent, origin)[2] - org[2]);
                                trace = steptrace2;
@@ -1326,6 +1331,14 @@ static int SV_FlyMove (prvm_edict_t *ent, float time, qbool applygravity, float
                        if (stepnormal)
                                VectorCopy(trace.plane.normal, stepnormal);
                }
+
+               // Unlike some other movetypes Quake's SV_FlyMove calls SV_Impact only after setting ONGROUND which id1 fiends rely on.
+               // If we stepped up (sv_gameplayfix_stepmultipletimes) this will impact the steptrace2 plane instead of the original.
+               if (PRVM_serveredictfloat(ent, solid) >= SOLID_TRIGGER && trace.ent)
+                       SV_Impact(ent, &trace);
+               if (ent->free)
+                       return blocked; // removed by the impact function
+
                if (trace.fraction >= 0.001)
                {
                        // actually covered some distance
@@ -1333,8 +1346,6 @@ static int SV_FlyMove (prvm_edict_t *ent, float time, qbool applygravity, float
                        numplanes = 0;
                }
 
-               time_left *= 1 - trace.fraction;
-
                // clipped to another plane
                if (numplanes >= MAX_CLIP_PLANES)
                {
@@ -1539,46 +1550,6 @@ static qbool SV_NudgeOutOfSolid_PivotIsKnownGood(prvm_edict_t *ent, vec3_t pivot
        return true;
 }
 
-qbool SV_NudgeOutOfSolid(prvm_edict_t *ent)
-{
-       prvm_prog_t *prog = SVVM_prog;
-       int bump, pass;
-       trace_t stucktrace;
-       vec3_t stuckorigin;
-       vec3_t stuckmins, stuckmaxs;
-       vec_t nudge;
-       vec_t separation = sv_gameplayfix_nudgeoutofsolid_separation.value;
-       if (sv.worldmodel && sv.worldmodel->brushq1.numclipnodes)
-               separation = 0.0f; // when using hulls, it can not be enlarged
-       VectorCopy(PRVM_serveredictvector(ent, mins), stuckmins);
-       VectorCopy(PRVM_serveredictvector(ent, maxs), stuckmaxs);
-       stuckmins[0] -= separation;
-       stuckmins[1] -= separation;
-       stuckmins[2] -= separation;
-       stuckmaxs[0] += separation;
-       stuckmaxs[1] += separation;
-       stuckmaxs[2] += separation;
-       // first pass we try to get it out of brush entities
-       // second pass we try to get it out of world only (can't win them all)
-       for (pass = 0;pass < 2;pass++)
-       {
-               VectorCopy(PRVM_serveredictvector(ent, origin), stuckorigin);
-               for (bump = 0;bump < 10;bump++)
-               {
-                       stucktrace = SV_TraceBox(stuckorigin, stuckmins, stuckmaxs, stuckorigin, pass ? MOVE_WORLDONLY : MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value);
-                       if (!stucktrace.bmodelstartsolid || stucktrace.startdepth >= 0)
-                       {
-                               // found a good location, use it
-                               VectorCopy(stuckorigin, PRVM_serveredictvector(ent, origin));
-                               return true;
-                       }
-                       nudge = -stucktrace.startdepth;
-                       VectorMA(stuckorigin, nudge, stucktrace.startdepthnormal, stuckorigin);
-               }
-       }
-       return false;
-}
-
 /*
 ============
 SV_PushEntity
@@ -1588,7 +1559,7 @@ The trace struct is filled with the trace that has been done.
 Returns true if the push did not result in the entity being teleported by QC code.
 ============
 */
-static qbool SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qbool dolink)
+static qbool SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qbool dolink, qbool checkstuck)
 {
        prvm_prog_t *prog = SVVM_prog;
        int solid;
@@ -1603,12 +1574,6 @@ static qbool SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboo
        VectorCopy(PRVM_serveredictvector(ent, mins), mins);
        VectorCopy(PRVM_serveredictvector(ent, maxs), maxs);
 
-       // move start position out of solids
-       if (sv_gameplayfix_nudgeoutofsolid.integer && sv_gameplayfix_nudgeoutofsolid_separation.value >= 0)
-       {
-               SV_NudgeOutOfSolid(ent);
-       }
-
        VectorCopy(PRVM_serveredictvector(ent, origin), start);
        VectorAdd(start, push, end);
 
@@ -1622,9 +1587,37 @@ static qbool SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboo
                type = MOVE_NORMAL;
 
        *trace = SV_TraceBox(start, mins, maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value);
-       // fail the move if stuck in world
-       if (trace->worldstartsolid)
-               return true;
+       // abort move if we're stuck in the world (and didn't make it out)
+       if (trace->worldstartsolid && trace->allsolid && trace->startdepth < 0 && checkstuck)
+       {
+               // checking startdepth eliminates many false positives on Q1BSP with mod_q1bsp_polygoncollisions 0
+               // but it's still not guaranteed that we're stuck in a bmodel at this point
+               if (sv_gameplayfix_nudgeoutofsolid.integer && sv_gameplayfix_nudgeoutofsolid_separation.value >= 0)
+               {
+                       switch (PHYS_NudgeOutOfSolid(prog, ent))
+                       {
+                               case 0:
+                                       Con_Printf(CON_WARN "NudgeOutOfSolid couldn't fix stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
+                                       return true; // definitely stuck in a bmodel
+                               case 1:
+                                       Con_DPrintf("NudgeOutOfSolid fixed stuck entity %i (classname \"%s\") with offset %f %f %f.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)), PRVM_serveredictvector(ent, origin)[0] - start[0], PRVM_serveredictvector(ent, origin)[1] - start[1], PRVM_serveredictvector(ent, origin)[2] - start[2]);
+                                       VectorCopy(PRVM_serveredictvector(ent, origin), start);
+                                       VectorAdd(start, push, end);
+                                       *trace = SV_TraceBox(start, mins, maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value);
+
+                               // definitely not stuck in a bmodel, move may proceed
+                       }
+               }
+               else if (sv_gameplayfix_unstickentities.integer && SV_UnstickEntity(ent))
+               {
+                       // bones_was_here: pretty sure we can deprecate sv_gameplayfix_unstickentities, sv_gameplayfix_nudgeoutofsolid is much nicer
+                       VectorCopy(PRVM_serveredictvector(ent, origin), start);
+                       VectorAdd(start, push, end);
+                       *trace = SV_TraceBox(start, mins, maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value);
+               }
+               else
+                       return true; // assuming stuck, bones_was_here TODO: always use PHYS_NudgeOutOfSolid (remove sv_gameplayfix_nudgeoutofsolid)?
+       }
 
        VectorCopy(trace->endpos, PRVM_serveredictvector(ent, origin));
 
@@ -1641,10 +1634,12 @@ static qbool SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboo
 #endif
 
        if (dolink)
+       {
                SV_LinkEdict_TouchAreaGrid(ent);
 
-       if((PRVM_serveredictfloat(ent, solid) >= SOLID_TRIGGER && trace->ent && (!((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND) || PRVM_serveredictedict(ent, groundentity) != PRVM_EDICT_TO_PROG(trace->ent))))
-               SV_Impact (ent, trace);
+               if((PRVM_serveredictfloat(ent, solid) >= SOLID_TRIGGER && trace->ent && (!((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND) || PRVM_serveredictedict(ent, groundentity) != PRVM_EDICT_TO_PROG(trace->ent))))
+                       SV_Impact (ent, trace);
+       }
 
        if(ent->priv.required->mark == PRVM_EDICT_MARK_SETORIGIN_CAUGHT)
        {
@@ -1890,7 +1885,7 @@ static void SV_PushMove (prvm_edict_t *pusher, float movetime)
 
                // try moving the contacted entity
                PRVM_serveredictfloat(pusher, solid) = SOLID_NOT;
-               if(!SV_PushEntity (&trace, check, move, true))
+               if(!SV_PushEntity(&trace, check, move, true, true))
                {
                        // entity "check" got teleported
                        PRVM_serveredictvector(check, angles)[1] += trace.fraction * moveangle[1];
@@ -1922,7 +1917,7 @@ static void SV_PushMove (prvm_edict_t *pusher, float movetime)
                        {
                                // hack to invoke all necessary movement triggers
                                VectorClear(move2);
-                               if(!SV_PushEntity(&trace2, check, move2, true))
+                               if(!SV_PushEntity(&trace2, check, move2, true, true))
                                {
                                        // entity "check" got teleported
                                        continue;
@@ -2350,7 +2345,11 @@ static void SV_WalkMove (prvm_edict_t *ent)
                VectorCopy(PRVM_serveredictvector(ent, maxs), entmaxs);
                trace = SV_TraceBox(upmove, entmins, entmaxs, downmove, type, ent, SV_GenericHitSuperContentsMask(ent), skipsupercontentsmask, skipmaterialflagsmask, collision_extendmovelength.value);
                if(trace.fraction < 1 && trace.plane.normal[2] > 0.7)
+               {
                        clip |= 1; // but we HAVE found a floor
+                       // set groundentity so we get carried when walking onto a mover with sv_gameplayfix_nogravityonground
+                       PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
+               }
        }
 
        // if the move did not hit the ground at any point, we're not on ground
@@ -2403,7 +2402,7 @@ static void SV_WalkMove (prvm_edict_t *ent)
                // move up
                VectorClear (upmove);
                upmove[2] = sv_stepheight.value;
-               if(!SV_PushEntity(&trace, ent, upmove, true))
+               if(!SV_PushEntity(&trace, ent, upmove, true, true))
                {
                        // we got teleported when upstepping... must abort the move
                        return;
@@ -2455,7 +2454,7 @@ static void SV_WalkMove (prvm_edict_t *ent)
        // move down
        VectorClear (downmove);
        downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
-       if(!SV_PushEntity (&downtrace, ent, downmove, true))
+       if(!SV_PushEntity(&downtrace, ent, downmove, true, true))
        {
                // we got teleported when downstepping... must abort the move
                return;
@@ -2657,19 +2656,12 @@ void SV_Physics_Toss (prvm_edict_t *ent)
        {
        // move origin
                VectorScale(PRVM_serveredictvector(ent, velocity), movetime, move);
-               if(!SV_PushEntity(&trace, ent, move, true))
+               // The buzzsaw traps in r2m6 and r2m7 use MOVETYPE_FLY and rely on moving while stuck in the world.
+               // Quake movetypes checked allsolid only in SV_FlyMove().
+               if(!SV_PushEntity(&trace, ent, move, true, PRVM_serveredictfloat(ent, movetype) != MOVETYPE_FLY))
                        return; // teleported
                if (ent->free)
                        return;
-               if (trace.bmodelstartsolid && sv_gameplayfix_unstickentities.integer)
-               {
-                       // try to unstick the entity
-                       SV_UnstickEntity(ent);
-                       if(!SV_PushEntity(&trace, ent, move, true))
-                               return; // teleported
-                       if (ent->free)
-                               return;
-               }
                if (trace.fraction == 1)
                        break;
                movetime *= 1 - min(1, trace.fraction);
@@ -2957,6 +2949,7 @@ static void SV_Physics_ClientEntity_NoThink (prvm_edict_t *ent)
        }
 }
 
+// asynchronous path
 void SV_Physics_ClientMove(void)
 {
        prvm_prog_t *prog = SVVM_prog;
index b90f748775029e8b2ff0b9640fdfef52bd768961..414e97a9b7a4716fee30f3bbc36cdb7a47cf1ee5 100644 (file)
--- a/sv_save.c
+++ b/sv_save.c
@@ -76,7 +76,7 @@ void SV_Savegame_to(prvm_prog_t *prog, const char *name)
                for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
                        FS_Printf(f, "%f\n", svs.clients[0].spawn_parms[i]);
                FS_Printf(f, "%d\n", current_skill);
-               FS_Printf(f, "%s\n", sv.name);
+               FS_Printf(f, "%s\n", sv.worldbasename);
                FS_Printf(f, "%f\n",sv.time);
        }
        else
@@ -231,7 +231,7 @@ void SV_Savegame_f(cmd_state_t *cmd)
                return;
        }
 
-       strlcpy (name, Cmd_Argv(cmd, 1), sizeof (name));
+       dp_strlcpy (name, Cmd_Argv(cmd, 1), sizeof (name));
        FS_DefaultExtension (name, ".sav", sizeof (name));
 
        SV_Savegame_to(prog, name);
@@ -265,7 +265,7 @@ void SV_Loadgame_f(cmd_state_t *cmd)
                return;
        }
 
-       strlcpy (filename, Cmd_Argv(cmd, 1), sizeof(filename));
+       dp_strlcpy (filename, Cmd_Argv(cmd, 1), sizeof(filename));
        FS_DefaultExtension (filename, ".sav", sizeof (filename));
 
        Con_Printf("Loading game from %s...\n", filename);
@@ -320,7 +320,7 @@ void SV_Loadgame_f(cmd_state_t *cmd)
 
        // mapname
        COM_ParseToken_Simple(&t, false, false, true);
-       strlcpy (mapname, com_token, sizeof(mapname));
+       dp_strlcpy (mapname, com_token, sizeof(mapname));
 
        if(developer_entityparsing.integer)
                Con_Printf("SV_Loadgame_f: loading time\n");
@@ -362,7 +362,7 @@ void SV_Loadgame_f(cmd_state_t *cmd)
                        t = start;
                        break;
                }
-               strlcpy(sv.lightstyles[i], com_token, sizeof(sv.lightstyles[i]));
+               dp_strlcpy(sv.lightstyles[i], com_token, sizeof(sv.lightstyles[i]));
        }
 
        if(developer_entityparsing.integer)
@@ -437,7 +437,7 @@ void SV_Loadgame_f(cmd_state_t *cmd)
                        PRVM_ED_ParseEdict (prog, start, ent);
 
                        // link it into the bsp tree
-                       if (!ent->free && !VectorCompare(PRVM_serveredictvector(ent, absmin), PRVM_serveredictvector(ent, absmax)))
+                       if (!ent->free)
                                SV_LinkEdict(ent);
                }
 
@@ -481,7 +481,7 @@ void SV_Loadgame_f(cmd_state_t *cmd)
                                        i = atoi(com_token);
                                        COM_ParseToken_Simple(&t, false, false, true);
                                        if (i >= 0 && i < MAX_LIGHTSTYLES)
-                                               strlcpy(sv.lightstyles[i], com_token, sizeof(sv.lightstyles[i]));
+                                               dp_strlcpy(sv.lightstyles[i], com_token, sizeof(sv.lightstyles[i]));
                                        else
                                                Con_Printf("unsupported lightstyle %i \"%s\"\n", i, com_token);
                                }
@@ -492,7 +492,7 @@ void SV_Loadgame_f(cmd_state_t *cmd)
                                        COM_ParseToken_Simple(&t, false, false, true);
                                        if (i >= 0 && i < MAX_MODELS)
                                        {
-                                               strlcpy(sv.model_precache[i], com_token, sizeof(sv.model_precache[i]));
+                                               dp_strlcpy(sv.model_precache[i], com_token, sizeof(sv.model_precache[i]));
                                                sv.models[i] = Mod_ForName (sv.model_precache[i], true, false, sv.model_precache[i][0] == '*' ? sv.worldname : NULL);
                                        }
                                        else
@@ -504,7 +504,7 @@ void SV_Loadgame_f(cmd_state_t *cmd)
                                        i = atoi(com_token);
                                        COM_ParseToken_Simple(&t, false, false, true);
                                        if (i >= 0 && i < MAX_SOUNDS)
-                                               strlcpy(sv.sound_precache[i], com_token, sizeof(sv.sound_precache[i]));
+                                               dp_strlcpy(sv.sound_precache[i], com_token, sizeof(sv.sound_precache[i]));
                                        else
                                                Con_Printf("unsupported sound %i \"%s\"\n", i, com_token);
                                }
index a1ee1050ab3cb69b14251c0be1bdc3b7ef165e27..a8a44347724791acfc0f179655e2c010ae2dd65a 100644 (file)
--- a/sv_send.c
+++ b/sv_send.c
@@ -22,7 +22,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include "sv_demo.h"
 
 extern cvar_t sv_airaccel_qw_stretchfactor;
-extern cvar_t sv_gameplayfix_customstats;
+extern cvar_t sv_qcstats;
 extern cvar_t sv_warsowbunny_airforwardaccel;
 extern cvar_t sv_warsowbunny_accel;
 extern cvar_t sv_warsowbunny_topspeed;
@@ -34,6 +34,7 @@ extern cvar_t sv_cullentities_trace_samples_players;
 extern cvar_t sv_cullentities_trace_eyejitter;
 extern cvar_t sv_cullentities_trace_expand;
 extern cvar_t sv_cullentities_trace_delay_players;
+extern cvar_t sv_cullentities_trace_spectators;
 
 /*
 =============================================================================
@@ -859,7 +860,7 @@ qbool SV_CanSeeBox(int numtraces, vec_t eyejitter, vec_t enlarge, vec_t entboxex
        return false;
 }
 
-void SV_MarkWriteEntityStateToClient(entity_state_t *s)
+void SV_MarkWriteEntityStateToClient(entity_state_t *s, client_t *client)
 {
        prvm_prog_t *prog = SVVM_prog;
        int isbmodel;
@@ -906,7 +907,7 @@ void SV_MarkWriteEntityStateToClient(entity_state_t *s)
                        // tag attached entities simply check their parent
                        if (!sv.sendentitiesindex[s->tagentity])
                                return;
-                       SV_MarkWriteEntityStateToClient(sv.sendentitiesindex[s->tagentity]);
+                       SV_MarkWriteEntityStateToClient(sv.sendentitiesindex[s->tagentity], client);
                        if (sv.sententities[s->tagentity] != sv.sententitiesmark)
                                return;
                }
@@ -946,7 +947,7 @@ void SV_MarkWriteEntityStateToClient(entity_state_t *s)
                        }
 
                        // or not seen by random tracelines
-                       if (sv_cullentities_trace.integer && !isbmodel && sv.worldmodel && sv.worldmodel->brush.TraceLineOfSight && !r_trippy.integer)
+                       if (sv_cullentities_trace.integer && !isbmodel && sv.worldmodel && sv.worldmodel->brush.TraceLineOfSight && !r_trippy.integer && (client->frags != -666 || sv_cullentities_trace_spectators.integer))
                        {
                                int samples =
                                        s->number <= svs.maxclients
@@ -955,13 +956,12 @@ void SV_MarkWriteEntityStateToClient(entity_state_t *s)
                                        s->specialvisibilityradius
                                                ? sv_cullentities_trace_samples_extra.integer
                                                : sv_cullentities_trace_samples.integer;
-                               float enlarge = sv_cullentities_trace_enlarge.value;
 
                                if(samples > 0)
                                {
                                        int eyeindex;
                                        for (eyeindex = 0;eyeindex < sv.writeentitiestoclient_numeyes;eyeindex++)
-                                               if(SV_CanSeeBox(samples, sv_cullentities_trace_eyejitter.value, enlarge, sv_cullentities_trace_expand.value, sv.writeentitiestoclient_eyes[eyeindex], ed->priv.server->cullmins, ed->priv.server->cullmaxs))
+                                               if(SV_CanSeeBox(samples, sv_cullentities_trace_eyejitter.value, sv_cullentities_trace_enlarge.value, sv_cullentities_trace_expand.value, sv.writeentitiestoclient_eyes[eyeindex], ed->priv.server->cullmins, ed->priv.server->cullmaxs))
                                                        break;
                                        if(eyeindex < sv.writeentitiestoclient_numeyes)
                                                svs.clients[sv.writeentitiestoclient_clientnumber].visibletime[s->number] =
@@ -970,7 +970,7 @@ void SV_MarkWriteEntityStateToClient(entity_state_t *s)
                                                                        ? sv_cullentities_trace_delay_players.value
                                                                        : sv_cullentities_trace_delay.value
                                                        );
-                                       else if (host.realtime > svs.clients[sv.writeentitiestoclient_clientnumber].visibletime[s->number])
+                                       else if ((float)host.realtime > svs.clients[sv.writeentitiestoclient_clientnumber].visibletime[s->number])
                                        {
                                                sv.writeentitiestoclient_stats_culled_trace++;
                                                return;
@@ -994,15 +994,12 @@ void SV_AddCameraEyes(void)
        prvm_prog_t *prog = SVVM_prog;
        int e, i, j, k;
        prvm_edict_t *ed;
-       static int cameras[MAX_LEVELNETWORKEYES];
-       static vec3_t camera_origins[MAX_LEVELNETWORKEYES];
-       static int eye_levels[MAX_CLIENTNETWORKEYES];
+       int cameras[MAX_LEVELNETWORKEYES];
+       vec3_t camera_origins[MAX_LEVELNETWORKEYES];
+       int eye_levels[MAX_CLIENTNETWORKEYES] = {0};
        int n_cameras = 0;
        vec3_t mi, ma;
 
-       for(i = 0; i < sv.writeentitiestoclient_numeyes; ++i)
-               eye_levels[i] = 0;
-
        // check line of sight to portal entities and add them to PVS
        for (e = 1, ed = PRVM_NEXT_EDICT(prog->edicts);e < prog->num_edicts;e++, ed = PRVM_NEXT_EDICT(ed))
        {
@@ -1044,7 +1041,12 @@ void SV_AddCameraEyes(void)
                for(k = 0; k < sv.writeentitiestoclient_numeyes; ++k)
                if(eye_levels[k] <= MAX_EYE_RECURSION)
                {
-                       if(SV_CanSeeBox(sv_cullentities_trace_samples.integer, sv_cullentities_trace_eyejitter.value, sv_cullentities_trace_enlarge.value, sv_cullentities_trace_expand.value, sv.writeentitiestoclient_eyes[k], mi, ma))
+                       if(SV_CanSeeBox(sv_cullentities_trace_samples_extra.integer, sv_cullentities_trace_eyejitter.value, sv_cullentities_trace_enlarge.value, sv_cullentities_trace_expand.value, sv.writeentitiestoclient_eyes[k], mi, ma))
+                               svs.clients[sv.writeentitiestoclient_clientnumber].visibletime[cameras[j]] = host.realtime + sv_cullentities_trace_delay.value;
+
+                       // bones_was_here: this use of visibletime doesn't conflict because sv_cullentities_trace doesn't consider portal entities
+                       // the explicit cast prevents float precision differences that cause the condition to fail
+                       if ((float)host.realtime <= svs.clients[sv.writeentitiestoclient_clientnumber].visibletime[cameras[j]])
                        {
                                eye_levels[sv.writeentitiestoclient_numeyes] = eye_levels[k] + 1;
                                VectorCopy(camera_origins[j], sv.writeentitiestoclient_eyes[sv.writeentitiestoclient_numeyes]);
@@ -1152,7 +1154,7 @@ void SV_WriteClientdataToMessage (client_t *client, prvm_edict_t *ent, sizebuf_t
        s = PRVM_GetString(prog, PRVM_serveredictstring(ent, weaponmodel));
        if (strcmp(s, client->weaponmodel))
        {
-               strlcpy(client->weaponmodel, s, sizeof(client->weaponmodel));
+               dp_strlcpy(client->weaponmodel, s, sizeof(client->weaponmodel));
                client->weaponmodelindex = SV_ModelIndex(s, 1);
        }
 
@@ -1203,7 +1205,7 @@ void SV_WriteClientdataToMessage (client_t *client, prvm_edict_t *ent, sizebuf_t
        //stats[STAT_SECRETS] = PRVM_serverglobalfloat(found_secrets);
        //stats[STAT_MONSTERS] = PRVM_serverglobalfloat(killed_monsters);
 
-       if(!sv_gameplayfix_customstats.integer)
+       if(!sv_qcstats.integer)
        {
                statsf[STAT_MOVEVARS_AIRACCEL_QW_STRETCHFACTOR] = sv_airaccel_qw_stretchfactor.value;
                statsf[STAT_MOVEVARS_AIRCONTROL_PENALTY] = sv_aircontrol_penalty.value;
@@ -1612,7 +1614,7 @@ static void SV_UpdateToReliableMessages (void)
                // always point the string back at host_client->name to keep it safe
                //strlcpy (host_client->name, name, sizeof (host_client->name));
                if (name != host_client->name) // prevent buffer overlap SIGABRT on Mac OSX
-                       strlcpy (host_client->name, name, sizeof (host_client->name));
+                       dp_strlcpy (host_client->name, name, sizeof (host_client->name));
                SV_Name(i);
 
                // DP_SV_CLIENTCOLORS
@@ -1633,7 +1635,7 @@ static void SV_UpdateToReliableMessages (void)
                // always point the string back at host_client->name to keep it safe
                //strlcpy (host_client->playermodel, model, sizeof (host_client->playermodel));
                if (model != host_client->playermodel) // prevent buffer overlap SIGABRT on Mac OSX
-                       strlcpy (host_client->playermodel, model, sizeof (host_client->playermodel));
+                       dp_strlcpy (host_client->playermodel, model, sizeof (host_client->playermodel));
                PRVM_serveredictstring(host_client->edict, playermodel) = PRVM_SetEngineString(prog, host_client->playermodel);
 
                // NEXUIZ_PLAYERSKIN
@@ -1643,7 +1645,7 @@ static void SV_UpdateToReliableMessages (void)
                // always point the string back at host_client->name to keep it safe
                //strlcpy (host_client->playerskin, skin, sizeof (host_client->playerskin));
                if (skin != host_client->playerskin) // prevent buffer overlap SIGABRT on Mac OSX
-                       strlcpy (host_client->playerskin, skin, sizeof (host_client->playerskin));
+                       dp_strlcpy (host_client->playerskin, skin, sizeof (host_client->playerskin));
                PRVM_serveredictstring(host_client->edict, playerskin) = PRVM_SetEngineString(prog, host_client->playerskin);
 
                // TODO: add an extension name for this [1/17/2008 Black]
index 6a621bac6d293c9ec2a573187e7ee821f1f805c9..58bef9bfb0a3f44a31ac9ec3270af7a91c19f75a 100644 (file)
--- a/sv_user.c
+++ b/sv_user.c
@@ -987,6 +987,7 @@ void SV_ReadClientMessage(void)
        prvm_prog_t *prog = SVVM_prog;
        int netcmd, num, start;
        char *s, *p, *q;
+       size_t slen;
 
        if(sv_autodemo_perclient.integer >= 2)
                SV_WriteDemoMessage(host_client, &(host_client->netconnection->message), true);
@@ -1035,7 +1036,8 @@ void SV_ReadClientMessage(void)
                        // allow reliable messages now as the client is done with initial loading
                        if (host_client->sendsignon == 2)
                                host_client->sendsignon = 0;
-                       s = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
+                       slen = MSG_ReadString_len(&sv_message, sv_readstring, sizeof(sv_readstring));
+                       s = sv_readstring;
                        q = NULL;
                        for(p = s; *p; ++p) switch(*p)
                        {
@@ -1054,19 +1056,19 @@ void SV_ReadClientMessage(void)
                        if (strncasecmp(s, "spawn", 5) == 0
                         || strncasecmp(s, "begin", 5) == 0
                         || strncasecmp(s, "prespawn", 8) == 0)
-                               Cmd_ExecuteString (cmd_serverfromclient, s, src_client, true);
+                               Cmd_ExecuteString(cmd_serverfromclient, s, slen, src_client, true);
                        else if (PRVM_serverfunction(SV_ParseClientCommand))
                        {
                                int restorevm_tempstringsbuf_cursize;
                                restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
-                               PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(prog, s);
+                               PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(prog, s, slen);
                                PRVM_serverglobalfloat(time) = sv.time;
                                PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict);
                                prog->ExecuteProgram(prog, PRVM_serverfunction(SV_ParseClientCommand), "QC function SV_ParseClientCommand is missing");
                                prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
                        }
                        else
-                               Cmd_ExecuteString (cmd_serverfromclient, s, src_client, true);
+                               Cmd_ExecuteString(cmd_serverfromclient, s, slen, src_client, true);
                        break;
 
 clc_stringcmd_invalid:
index 672ff70d1f8e7116237d6f81737fe2b8012a014b..4ad07559b7473ba25fbf1fe0007c88c3ee19795a 100644 (file)
@@ -100,6 +100,7 @@ const char *vm_sv_extensions[] = {
 "DP_QC_ENTITYSTRING",
 "DP_QC_ETOS",
 "DP_QC_EXTRESPONSEPACKET",
+"DP_QC_FINDBOX",
 "DP_QC_FINDCHAIN",
 "DP_QC_FINDCHAINFLAGS",
 "DP_QC_FINDCHAINFLOAT",
@@ -107,6 +108,7 @@ const char *vm_sv_extensions[] = {
 "DP_QC_FINDFLAGS",
 "DP_QC_FINDFLOAT",
 "DP_QC_FS_SEARCH",
+"DP_QC_FS_SEARCH_PACKFILE",
 "DP_QC_GETLIGHT",
 "DP_QC_GETSURFACE",
 "DP_QC_GETSURFACETRIANGLE",
@@ -119,6 +121,7 @@ const char *vm_sv_extensions[] = {
 "DP_QC_LOG",
 "DP_QC_MINMAXBOUND",
 "DP_QC_MULTIPLETEMPSTRINGS",
+"DP_QC_NUDGEOUTOFSOLID",
 "DP_QC_NUM_FOR_EDICT",
 "DP_QC_RANDOMVEC",
 "DP_QC_SINCOSSQRTPOW",
@@ -228,7 +231,6 @@ const char *vm_sv_extensions[] = {
 "TW_SV_STEPCONTROL",
 "ZQ_PAUSE",
 "DP_RM_CLIPGROUP",
-"DP_QC_FS_SEARCH_PACKFILE",
 NULL
 //"EXT_CSQC" // not ready yet
 };
@@ -373,7 +375,7 @@ static void VM_SV_sprint(prvm_prog_t *prog)
 {
        client_t        *client;
        int                     entnum;
-       char string[VM_STRINGTEMP_LENGTH];
+       char string[VM_TEMPSTRING_MAXSIZE];
 
        VM_SAFEPARMCOUNTRANGE(2, 8, VM_SV_sprint);
 
@@ -415,7 +417,7 @@ static void VM_SV_centerprint(prvm_prog_t *prog)
 {
        client_t        *client;
        int                     entnum;
-       char string[VM_STRINGTEMP_LENGTH];
+       char string[VM_TEMPSTRING_MAXSIZE];
 
        VM_SAFEPARMCOUNTRANGE(2, 8, VM_SV_centerprint);
 
@@ -660,7 +662,7 @@ static void VM_SV_traceline(prvm_prog_t *prog)
        move = (int)PRVM_G_FLOAT(OFS_PARM2);
        ent = PRVM_G_EDICT(OFS_PARM3);
 
-       if (VEC_IS_NAN(v1[0]) || VEC_IS_NAN(v1[1]) || VEC_IS_NAN(v1[2]) || VEC_IS_NAN(v2[0]) || VEC_IS_NAN(v2[1]) || VEC_IS_NAN(v2[2]))
+       if (isnan(v1[0]) || isnan(v1[1]) || isnan(v1[2]) || isnan(v2[0]) || isnan(v2[1]) || isnan(v2[2]))
                prog->error_cmd("%s: NAN errors detected in traceline('%f %f %f', '%f %f %f', %i, entity %i)\n", prog->name, v1[0], v1[1], v1[2], v2[0], v2[1], v2[2], move, PRVM_EDICT_TO_PROG(ent));
 
        trace = SV_TraceLine(v1, v2, move, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendtracelinelength.value);
@@ -699,7 +701,7 @@ static void VM_SV_tracebox(prvm_prog_t *prog)
        move = (int)PRVM_G_FLOAT(OFS_PARM4);
        ent = PRVM_G_EDICT(OFS_PARM5);
 
-       if (VEC_IS_NAN(v1[0]) || VEC_IS_NAN(v1[1]) || VEC_IS_NAN(v1[2]) || VEC_IS_NAN(v2[0]) || VEC_IS_NAN(v2[1]) || VEC_IS_NAN(v2[2]))
+       if (isnan(v1[0]) || isnan(v1[1]) || isnan(v1[2]) || isnan(v2[0]) || isnan(v2[1]) || isnan(v2[2]))
                prog->error_cmd("%s: NAN errors detected in tracebox('%f %f %f', '%f %f %f', '%f %f %f', '%f %f %f', %i, entity %i)\n", prog->name, v1[0], v1[1], v1[2], m1[0], m1[1], m1[2], m2[0], m2[1], m2[2], v2[0], v2[1], v2[2], move, PRVM_EDICT_TO_PROG(ent));
 
        trace = SV_TraceBox(v1, m1, m2, v2, move, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendtraceboxlength.value);
@@ -960,7 +962,7 @@ static void VM_SV_stuffcmd(prvm_prog_t *prog)
 {
        int             entnum;
        client_t        *old;
-       char    string[VM_STRINGTEMP_LENGTH];
+       char    string[VM_TEMPSTRING_MAXSIZE];
 
        VM_SAFEPARMCOUNTRANGE(2, 8, VM_SV_stuffcmd);
 
@@ -1005,7 +1007,7 @@ static void VM_SV_findradius(prvm_prog_t *prog)
        else
                chainfield = prog->fieldoffsets.chain;
        if (chainfield < 0)
-               prog->error_cmd("VM_findchain: %s doesnt have the specified chain field !", prog->name);
+               prog->error_cmd("VM_SV_findradius: %s doesnt have the specified chain field !", prog->name);
 
        chain = (prvm_edict_t *)prog->edicts;
 
@@ -1056,6 +1058,50 @@ static void VM_SV_findradius(prvm_prog_t *prog)
        VM_RETURN_EDICT(chain);
 }
 
+/*
+=================
+VM_SV_findbox
+
+Returns a chain of entities that are touching a box (a simpler findradius); supports DP_QC_FINDCHAIN_TOFIELD
+
+findbox (mins, maxs)
+=================
+*/
+static void VM_SV_findbox(prvm_prog_t *prog)
+{
+       prvm_edict_t *chain;
+       int i, numtouchedicts;
+       static prvm_edict_t *touchedicts[MAX_EDICTS];
+       int chainfield;
+
+       VM_SAFEPARMCOUNTRANGE(2, 3, VM_SV_findbox);
+
+       if(prog->argc == 3)
+               chainfield = PRVM_G_INT(OFS_PARM2);
+       else
+               chainfield = prog->fieldoffsets.chain;
+       if (chainfield < 0)
+               prog->error_cmd("VM_SV_findbox: %s doesnt have the specified chain field !", prog->name);
+
+       chain = (prvm_edict_t *)prog->edicts;
+
+       numtouchedicts = SV_EntitiesInBox(PRVM_G_VECTOR(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM1), MAX_EDICTS, touchedicts);
+       if (numtouchedicts > MAX_EDICTS)
+       {
+               // this never happens
+               Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
+               numtouchedicts = MAX_EDICTS;
+       }
+       for (i = 0; i < numtouchedicts; ++i)
+       {
+               prog->xfunction->builtinsprofile++;
+               PRVM_EDICTFIELDEDICT(touchedicts[i], chainfield) = PRVM_EDICT_TO_PROG(chain);
+               chain = touchedicts[i];
+       }
+
+       VM_RETURN_EDICT(chain);
+}
+
 static void VM_SV_precache_sound(prvm_prog_t *prog)
 {
        VM_SAFEPARMCOUNT(1, VM_SV_precache_sound);
@@ -1133,12 +1179,18 @@ VM_SV_droptofloor
 void() droptofloor
 ===============
 */
-
+inline static qbool droptofloor_bsp_failcond(trace_t *trace)
+{
+       if (sv.worldmodel->brush.isq3bsp || sv.worldmodel->brush.isq2bsp)
+               return trace->startsolid;
+       else
+               return trace->allsolid || trace->fraction == 1;
+}
 static void VM_SV_droptofloor(prvm_prog_t *prog)
 {
-       prvm_edict_t            *ent;
-       vec3_t          end, entorigin, entmins, entmaxs;
-       trace_t         trace;
+       prvm_edict_t *ent;
+       vec3_t        end;
+       trace_t       trace;
 
        VM_SAFEPARMCOUNTRANGE(0, 2, VM_SV_droptofloor); // allow 2 parameters because the id1 defs.qc had an incorrect prototype
 
@@ -1157,58 +1209,71 @@ static void VM_SV_droptofloor(prvm_prog_t *prog)
                return;
        }
 
-       VectorCopy (PRVM_serveredictvector(ent, origin), end);
-       end[2] -= 256;
-
        if (sv_gameplayfix_droptofloorstartsolid_nudgetocorrect.integer)
-               SV_NudgeOutOfSolid(ent);
-
-       VectorCopy(PRVM_serveredictvector(ent, origin), entorigin);
-       VectorCopy(PRVM_serveredictvector(ent, mins), entmins);
-       VectorCopy(PRVM_serveredictvector(ent, maxs), entmaxs);
-       trace = SV_TraceBox(entorigin, entmins, entmaxs, end, MOVE_NORMAL, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value);
-       if (trace.startsolid && sv_gameplayfix_droptofloorstartsolid.integer)
-       {
-               vec3_t offset, org;
-               VectorSet(offset, 0.5f * (PRVM_serveredictvector(ent, mins)[0] + PRVM_serveredictvector(ent, maxs)[0]), 0.5f * (PRVM_serveredictvector(ent, mins)[1] + PRVM_serveredictvector(ent, maxs)[1]), PRVM_serveredictvector(ent, mins)[2]);
-               VectorAdd(PRVM_serveredictvector(ent, origin), offset, org);
-               trace = SV_TraceLine(org, end, MOVE_NORMAL, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value);
-               VectorSubtract(trace.endpos, offset, trace.endpos);
-               if (trace.startsolid)
-               {
-                       Con_DPrintf("droptofloor at %f %f %f - COULD NOT FIX BADLY PLACED ENTITY\n", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
-                       SV_LinkEdict(ent);
-                       PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
-                       PRVM_serveredictedict(ent, groundentity) = 0;
-                       PRVM_G_FLOAT(OFS_RETURN) = 1;
-               }
-               else if (trace.fraction < 1)
+       {
+               int n = PHYS_NudgeOutOfSolid(prog, ent);
+               if (!n)
+                       VM_Warning(prog, "droptofloor at \"%f %f %f\": sv_gameplayfix_droptofloorstartsolid_nudgetocorrect COULD NOT FIX badly placed entity \"%s\" before drop\n", PRVM_gameedictvector(ent, origin)[0], PRVM_gameedictvector(ent, origin)[1], PRVM_gameedictvector(ent, origin)[2], PRVM_GetString(prog, PRVM_gameedictstring(ent, classname)));
+               else if (n > 0)
+                       VM_Warning(prog, "droptofloor at \"%f %f %f\": sv_gameplayfix_droptofloorstartsolid_nudgetocorrect FIXED badly placed entity \"%s\" before drop\n", PRVM_gameedictvector(ent, origin)[0], PRVM_gameedictvector(ent, origin)[1], PRVM_gameedictvector(ent, origin)[2], PRVM_GetString(prog, PRVM_gameedictstring(ent, classname)));
+       }
+
+       VectorCopy (PRVM_serveredictvector(ent, origin), end);
+       if (sv.worldmodel->brush.isq3bsp)
+               end[2] -= 4096;
+       else if (sv.worldmodel->brush.isq2bsp)
+               end[2] -= 128;
+       else
+               end[2] -= 256; // Quake, QuakeWorld
+
+       /* bones_was_here: not using SV_GenericHitSuperContentsMask(ent) anymore because it was setting:
+        * items:    SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY
+        * monsters: SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP
+        * explobox: SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE
+        * which caused (startsolid == true) when, for example, a health was touching a monster.
+        * Changing MOVE_NORMAL also fixes that, but other engines are using MOVE_NORMAL here.
+        */
+       trace = SV_TraceBox(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs), end, MOVE_NORMAL, ent, SUPERCONTENTS_SOLID, 0, 0, collision_extendmovelength.value);
+       if (droptofloor_bsp_failcond(&trace))
+       {
+               if (sv_gameplayfix_droptofloorstartsolid.integer)
                {
-                       Con_DPrintf("droptofloor at %f %f %f - FIXED BADLY PLACED ENTITY\n", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
-                       VectorCopy (trace.endpos, PRVM_serveredictvector(ent, origin));
+                       vec3_t offset, org;
+
+                       offset[0] = 0.5f * (PRVM_serveredictvector(ent, mins)[0] + PRVM_serveredictvector(ent, maxs)[0]);
+                       offset[1] = 0.5f * (PRVM_serveredictvector(ent, mins)[1] + PRVM_serveredictvector(ent, maxs)[1]);
+                       offset[2] = PRVM_serveredictvector(ent, mins)[2];
+                       VectorAdd(PRVM_serveredictvector(ent, origin), offset, org);
+                       VectorAdd(end, offset, end);
+
+                       trace = SV_TraceLine(org, end, MOVE_NORMAL, ent, SUPERCONTENTS_SOLID, 0, 0, collision_extendmovelength.value);
+                       if (droptofloor_bsp_failcond(&trace))
+                       {
+                               VM_Warning(prog, "droptofloor at \"%f %f %f\": sv_gameplayfix_droptofloorstartsolid COULD NOT FIX badly placed entity \"%s\"\n", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2], PRVM_GetString(prog, PRVM_gameedictstring(ent, classname)));
+                               return;
+                       }
+                       VM_Warning(prog, "droptofloor at \"%f %f %f\": sv_gameplayfix_droptofloorstartsolid FIXED badly placed entity \"%s\"\n", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2], PRVM_GetString(prog, PRVM_gameedictstring(ent, classname)));
+                       VectorSubtract(trace.endpos, offset, PRVM_serveredictvector(ent, origin));
+
+                       // only because we dropped it without considering its bbox
                        if (sv_gameplayfix_droptofloorstartsolid_nudgetocorrect.integer)
-                               SV_NudgeOutOfSolid(ent);
-                       SV_LinkEdict(ent);
-                       PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
-                       PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
-                       PRVM_G_FLOAT(OFS_RETURN) = 1;
-                       // if support is destroyed, keep suspended (gross hack for floating items in various maps)
-                       ent->priv.server->suspendedinairflag = true;
+                               PHYS_NudgeOutOfSolid(prog, ent);
                }
-       }
-       else
-       {
-               if (!trace.allsolid && trace.fraction < 1)
+               else
                {
-                       VectorCopy (trace.endpos, PRVM_serveredictvector(ent, origin));
-                       SV_LinkEdict(ent);
-                       PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
-                       PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
-                       PRVM_G_FLOAT(OFS_RETURN) = 1;
-                       // if support is destroyed, keep suspended (gross hack for floating items in various maps)
-                       ent->priv.server->suspendedinairflag = true;
+                       VM_Warning(prog, "droptofloor at \"%f %f %f\": badly placed entity \"%s\", startsolid: %d allsolid: %d\n", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2], PRVM_GetString(prog, PRVM_gameedictstring(ent, classname)), trace.startsolid, trace.allsolid);
+                       return;
                }
        }
+       else
+               VectorCopy(trace.endpos, PRVM_serveredictvector(ent, origin));
+
+       SV_LinkEdict(ent);
+       PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
+       PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
+       PRVM_G_FLOAT(OFS_RETURN) = 1;
+       // if support is destroyed, keep suspended (gross hack for floating items in various maps)
+       ent->priv.server->suspendedinairflag = true;
 }
 
 /*
@@ -1235,7 +1300,7 @@ static void VM_SV_lightstyle(prvm_prog_t *prog)
        }
 
 // change the string in sv
-       strlcpy(sv.lightstyles[style], val, sizeof(sv.lightstyles[style]));
+       dp_strlcpy(sv.lightstyles[style], val, sizeof(sv.lightstyles[style]));
 
 // send message to all clients on this server
        if (sv.state != ss_active)
@@ -1404,11 +1469,21 @@ static sizebuf_t *WriteDest(prvm_prog_t *prog)
        case MSG_ONE:
                ent = PRVM_PROG_TO_EDICT(PRVM_serverglobaledict(msg_entity));
                entnum = PRVM_NUM_FOR_EDICT(ent);
-               if (entnum < 1 || entnum > svs.maxclients || !svs.clients[entnum-1].active || !svs.clients[entnum-1].netconnection)
+               if (entnum < 1 || entnum > svs.maxclients)
                {
                        VM_Warning(prog, "WriteDest: tried to write to non-client\n");
                        return &sv.reliable_datagram;
                }
+               else if (!svs.clients[entnum-1].active)
+               {
+                       VM_Warning(prog, "WriteDest: tried to write to a disconnected client\n");
+                       return &sv.reliable_datagram;
+               }
+               else if (!svs.clients[entnum-1].netconnection)
+               {
+                       VM_Warning(prog, "WriteDest: tried to write to a bot client\n");
+                       return &sv.reliable_datagram;
+               }
                else
                        return &svs.clients[entnum-1].netconnection->message;
 
@@ -1665,7 +1740,7 @@ void VM_SV_UpdateCustomStats (client_t *client, prvm_edict_t *ent, sizebuf_t *ms
                //string as 16 bytes
                case 1:
                        memset(s, 0, 17);
-                       strlcpy(s, PRVM_E_STRING(ent, vm_customstats[i].fieldoffset), 16);
+                       dp_strlcpy(s, PRVM_E_STRING(ent, vm_customstats[i].fieldoffset), 16);
                        stats[i] = s[ 0] + s[ 1] * 256 + s[ 2] * 65536 + s[ 3] * 16777216;
                        stats[i+1] = s[ 4] + s[ 5] * 256 + s[ 6] * 65536 + s[ 7] * 16777216;
                        stats[i+2] = s[ 8] + s[ 9] * 256 + s[10] * 65536 + s[11] * 16777216;
@@ -1687,7 +1762,7 @@ void VM_SV_UpdateCustomStats (client_t *client, prvm_edict_t *ent, sizebuf_t *ms
        }
 }
 
-extern cvar_t sv_gameplayfix_customstats;
+extern cvar_t sv_qcstats;
 
 // void(float index, float type, .void field) SV_AddStat = #232;
 // Set up an auto-sent player stat.
@@ -1738,9 +1813,9 @@ static void VM_SV_AddStat(prvm_prog_t *prog)
        // these are hazardous to override but sort of allowed if one wants to be adventurous...  and enjoys warnings.
        if (i < MIN_VM_STAT)
                VM_Warning(prog, "PF_SV_AddStat: index (%i) < MIN_VM_STAT (%i) may conflict with engine stats - allowed, but this may break things\n", i, MIN_VM_STAT);
-       else if (i >= MAX_VM_STAT && !sv_gameplayfix_customstats.integer)
+       else if (i >= MAX_VM_STAT && !sv_qcstats.integer)
                VM_Warning(prog, "PF_SV_AddStat: index (%i) >= MAX_VM_STAT (%i) conflicts with engine stats - allowed, but this may break slowmo and stuff\n", i, MAX_VM_STAT);
-       else if (i > (MAX_VM_STAT - 4) && type == 1 && !sv_gameplayfix_customstats.integer)
+       else if (i > (MAX_VM_STAT - 4) && type == 1 && !sv_qcstats.integer)
                VM_Warning(prog, "PF_SV_AddStat: index (%i) >= MAX_VM_STAT (%i) - 4 with string type won't fit within MAX_VM_STAT, thus conflicting with engine stats - allowed, but this may break slowmo and stuff\n", i, MAX_VM_STAT);
 
        vm_customstats[i].type          = type;
@@ -1785,8 +1860,7 @@ static void VM_SV_copyentity(prvm_prog_t *prog)
                return;
        }
        memcpy(out->fields.fp, in->fields.fp, prog->entityfields * sizeof(prvm_vec_t));
-       if (VectorCompare(PRVM_serveredictvector(out, absmin), PRVM_serveredictvector(out, absmax)))
-               return;
+
        SV_LinkEdict(out);
 }
 
@@ -2359,7 +2433,7 @@ static void VM_SV_clientcommand(prvm_prog_t *prog)
 
        temp_client = host_client;
        host_client = svs.clients + i;
-       Cmd_ExecuteString(cmd_serverfromclient, PRVM_G_STRING(OFS_PARM1), src_client, true);
+       Cmd_ExecuteString(cmd_serverfromclient, PRVM_G_STRING(OFS_PARM1), strlen(PRVM_G_STRING(OFS_PARM1)), src_client, true);
        host_client = temp_client;
 }
 
@@ -2612,7 +2686,7 @@ static void VM_SV_gettaginfo(prvm_prog_t *prog)
        Matrix4x4_ToVectors(&tag_localmatrix, forward, left, up, origin);
 
        PRVM_serverglobalfloat(gettaginfo_parent) = parentindex;
-       PRVM_serverglobalstring(gettaginfo_name) = tagname ? PRVM_SetTempString(prog, tagname) : 0;
+       PRVM_serverglobalstring(gettaginfo_name) = tagname ? PRVM_SetTempString(prog, tagname, strlen(tagname)) : 0;
        VectorCopy(forward, PRVM_serverglobalvector(gettaginfo_forward));
        VectorNegate(left, PRVM_serverglobalvector(gettaginfo_right));
        VectorCopy(up, PRVM_serverglobalvector(gettaginfo_up));
@@ -2692,13 +2766,13 @@ static void VM_SV_clienttype(prvm_prog_t *prog)
        VM_SAFEPARMCOUNT(1, VM_SV_clienttype);
        clientnum = PRVM_G_EDICTNUM(OFS_PARM0) - 1;
        if (clientnum < 0 || clientnum >= svs.maxclients)
-               PRVM_G_FLOAT(OFS_RETURN) = 3;
+               PRVM_G_FLOAT(OFS_RETURN) = 3; // CLIENTTYPE_NOTACLIENT
        else if (!svs.clients[clientnum].active)
-               PRVM_G_FLOAT(OFS_RETURN) = 0;
+               PRVM_G_FLOAT(OFS_RETURN) = 0; // CLIENTTYPE_DISCONNECTED
        else if (svs.clients[clientnum].netconnection)
-               PRVM_G_FLOAT(OFS_RETURN) = 1;
+               PRVM_G_FLOAT(OFS_RETURN) = 1; // CLIENTTYPE_REAL
        else
-               PRVM_G_FLOAT(OFS_RETURN) = 2;
+               PRVM_G_FLOAT(OFS_RETURN) = 2; // CLIENTTYPE_BOT
 }
 
 /*
@@ -2710,10 +2784,11 @@ string(string key) serverkey
 */
 static void VM_SV_serverkey(prvm_prog_t *prog)
 {
-       char string[VM_STRINGTEMP_LENGTH];
+       char string[VM_TEMPSTRING_MAXSIZE];
+
        VM_SAFEPARMCOUNT(1, VM_SV_serverkey);
        InfoString_GetValue(svs.serverinfo, PRVM_G_STRING(OFS_PARM0), string, sizeof(string));
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, string);
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, string, strlen(string));
 }
 
 //#333 void(entity e, float mdlindex) setmodelindex (EXT_CSQC)
@@ -2850,18 +2925,17 @@ static void VM_SV_pointparticles(prvm_prog_t *prog)
        SV_FlushBroadcastMessages();
 }
 
-qbool SV_VM_ConsoleCommand (const char *text)
+qbool SV_VM_ConsoleCommand(const char *text, size_t textlen)
 {
        prvm_prog_t *prog = SVVM_prog;
-       return PRVM_ConsoleCommand(prog, text, &prog->funcoffsets.ConsoleCmd, true, PRVM_EDICT_TO_PROG(sv.world.prog->edicts), sv.time,  !(!sv.active || !prog || !prog->loaded), "QC function ConsoleCmd is missing"); 
+       return PRVM_ConsoleCommand(prog, text, textlen, &prog->funcoffsets.ConsoleCmd, true, PRVM_EDICT_TO_PROG(sv.world.prog->edicts), sv.time, "QC function ConsoleCmd is missing");
 }
 
 // #352 void(string cmdname) registercommand (EXT_CSQC)
 static void VM_SV_registercommand (prvm_prog_t *prog)
 {
        VM_SAFEPARMCOUNT(1, VM_SV_registercmd);
-       if(!Cmd_Exists(cmd_local, PRVM_G_STRING(OFS_PARM0)))
-               Cmd_AddCommand(CF_SERVER, PRVM_G_STRING(OFS_PARM0), NULL, "console command created by QuakeC");
+       Cmd_AddCommand(CF_SERVER, PRVM_G_STRING(OFS_PARM0), NULL, "console command created by QuakeC");
 }
 
 //PF_setpause,    // void(float pause) setpause        = #531;
@@ -2970,7 +3044,7 @@ static void VM_SV_skel_get_bonename(prvm_prog_t *prog)
                return;
        if (bonenum < 0 || bonenum >= skeleton->model->num_bones)
                return;
-       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, skeleton->model->data_bones[bonenum].name);
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, skeleton->model->data_bones[bonenum].name, strlen(skeleton->model->data_bones[bonenum].name));
 }
 
 // #267 float(float skel, float bonenum) skel_get_boneparent = #267; // (FTE_CSQC_SKELETONOBJECTS) returns parent num for supplied bonenum, 0 if bonenum has no parent or bone does not exist (returned value is always less than bonenum, you can loop on this)
@@ -3236,7 +3310,7 @@ NULL,                                                     // #42 (QUAKE)
 VM_fabs,                                               // #43 float(float f) fabs (QUAKE)
 VM_SV_aim,                                             // #44 vector(entity e, float speed) aim (QUAKE)
 VM_cvar,                                               // #45 float(string s) cvar (QUAKE)
-VM_localcmd_server,                            // #46 void(string s) localcmd (QUAKE)
+VM_localcmd,                                   // #46 void(string s) localcmd (QUAKE)
 VM_nextent,                                            // #47 entity(entity e) nextent (QUAKE)
 VM_SV_particle,                                        // #48 void(vector o, vector d, float color, float count) particle (QUAKE)
 VM_changeyaw,                                  // #49 void() ChangeYaw (QUAKE)
@@ -3306,7 +3380,7 @@ VM_fclose,                                                // #111 void(float fhandle) fclose (FRIK_FILE)
 VM_fgets,                                              // #112 string(float fhandle) fgets (FRIK_FILE)
 VM_fputs,                                              // #113 void(float fhandle, string s) fputs (FRIK_FILE)
 VM_strlen,                                             // #114 float(string s) strlen (FRIK_FILE)
-VM_strcat,                                             // #115 string(string s1, string s2, ...) strcat (FRIK_FILE)
+VM_strcat,                                             // #115 string(string s, string...) strcat (FRIK_FILE)
 VM_substring,                                  // #116 string(string s, float start, float length) substring (FRIK_FILE)
 VM_stov,                                               // #117 vector(string) stov (FRIK_FILE)
 VM_strzone,                                            // #118 string(string s) strzone (FRIK_FILE)
@@ -3760,8 +3834,8 @@ NULL,                                                     // #562
 NULL,                                                  // #563
 NULL,                                                  // #564
 NULL,                                                  // #565
-NULL,                                                  // #566
-NULL,                                                  // #567
+VM_SV_findbox,                                 // #566 entity(vector mins, vector maxs) findbox = #566; (DP_QC_FINDBOX)
+VM_nudgeoutofsolid,                            // #567 float(entity ent) nudgeoutofsolid = #567; (DP_QC_NUDGEOUTOFSOLID)
 NULL,                                                  // #568
 NULL,                                                  // #569
 NULL,                                                  // #570
@@ -3850,14 +3924,5 @@ void SVVM_init_cmd(prvm_prog_t *prog)
 void SVVM_reset_cmd(prvm_prog_t *prog)
 {
        World_End(&sv.world);
-
-       if(prog->loaded && PRVM_serverfunction(SV_Shutdown))
-       {
-               func_t s = PRVM_serverfunction(SV_Shutdown);
-               PRVM_serverglobalfloat(time) = sv.time;
-               PRVM_serverfunction(SV_Shutdown) = 0; // prevent it from getting called again
-               prog->ExecuteProgram(prog, s,"SV_Shutdown() required");
-       }
-
        VM_Cmd_Reset(prog);
 }
diff --git a/sys.h b/sys.h
index eb99088fee0c5c7100d36743c375a7725f787ce0..a5c6b275b2e08d9d2c5e2d45251d4c8a1e3d5e11 100644 (file)
--- a/sys.h
+++ b/sys.h
@@ -42,14 +42,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #endif
 # define DP_MOBILETOUCH        1
 # define DP_FREETYPE_STATIC 1
-#elif TARGET_OS_IPHONE /* must come first because it also defines MACOSX */
-# define DP_OS_NAME            "iPhoneOS"
-# define DP_OS_STR             "iphoneos"
-# define USE_GLES2             1
-# define LINK_TO_ZLIB  1
-# define LINK_TO_LIBVORBIS 1
-# define DP_MOBILETOUCH        1
-# define DP_FREETYPE_STATIC 1
 #elif defined(__linux__)
 # define DP_OS_NAME            "Linux"
 # define DP_OS_STR             "linux"
@@ -71,9 +63,19 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #elif defined(__DragonFly__)
 # define DP_OS_NAME            "DragonFlyBSD"
 # define DP_OS_STR             "dragonflybsd"
-#elif defined(MACOSX)
-# define DP_OS_NAME            "Mac OS X"
-# define DP_OS_STR             "osx"
+#elif defined(__APPLE__)
+# if TARGET_OS_IPHONE
+#  define DP_OS_NAME "iOS"
+#  define DP_OS_STR "ios"
+#  define USE_GLES2              1
+#  define LINK_TO_ZLIB   1
+#  define LINK_TO_LIBVORBIS 1
+#  define DP_MOBILETOUCH 1
+#  define DP_FREETYPE_STATIC 1
+# elif TARGET_OS_MAC
+#  define DP_OS_NAME "macOS"
+#  define DP_OS_STR "macos"
+# endif 
 #elif defined(__MORPHOS__)
 # define DP_OS_NAME            "MorphOS"
 # define DP_OS_STR             "morphos"
@@ -148,7 +150,6 @@ typedef struct sys_s
 
 extern sys_t sys;
 
-extern struct cvar_s sys_usenoclockbutbenchmark;
 
 //
 // DLL management
@@ -203,16 +204,19 @@ char *Sys_TimeString(const char *timeformat);
 // system IO interface (these are the sys functions that need to be implemented in a new driver atm)
 //
 
-/// an error will cause the entire program to exit
+/// Causes the entire program to exit ASAP.
+/// Trailing \n should be omitted.
 void Sys_Error (const char *error, ...) DP_FUNC_PRINTF(1) DP_FUNC_NORETURN;
 
 /// (may) output text to terminal which launched program
-void Sys_Print(const char *text);
+/// is POSIX async-signal-safe
+/// textlen excludes any (optional) \0 terminator
+void Sys_Print(const char *text, size_t textlen);
+/// used to report failures inside Con_Printf()
 void Sys_Printf(const char *fmt, ...);
 
 /// INFO: This is only called by Host_Shutdown so we dont need testing for recursion
-void Sys_Shutdown (void);
-void Sys_Quit (int returnvalue);
+void Sys_SDL_Shutdown(void);
 
 /*! on some build/platform combinations (such as Linux gcc with the -pg
  * profiling option) this can turn on/off profiling, used primarily to limit
@@ -235,15 +239,18 @@ double Sys_DirtyTime(void);
 
 void Sys_ProvideSelfFD (void);
 
+/// Reads a line from POSIX stdin or the Windows console
 char *Sys_ConsoleInput (void);
 
 /// called to yield for a little bit so as not to hog cpu when paused or debugging
-void Sys_Sleep(int microseconds);
+double Sys_Sleep(double time);
 
+void Sys_SDL_Dialog(const char *title, const char *string);
+void Sys_SDL_Init(void);
 /// Perform Key_Event () callbacks until the input que is empty
-void Sys_SendKeyEvents (void);
+void Sys_SDL_HandleEvents(void);
 
-char *Sys_GetClipboardData (void);
+char *Sys_SDL_GetClipboardData (void);
 
 extern qbool sys_supportsdlgetticks;
 unsigned int Sys_SDL_GetTicks (void); // wrapper to call SDL_GetTicks
@@ -254,5 +261,7 @@ void Sys_InitProcessNice (void);
 void Sys_MakeProcessNice (void);
 void Sys_MakeProcessMean (void);
 
+int Sys_Main(int argc, char *argv[]);
+
 #endif
 
diff --git a/sys_null.c b/sys_null.c
new file mode 100644 (file)
index 0000000..bb28858
--- /dev/null
@@ -0,0 +1,40 @@
+
+#include "darkplaces.h"
+
+
+// =======================================================================
+// General routines
+// =======================================================================
+
+void Sys_SDL_Shutdown(void)
+{
+}
+
+void Sys_SDL_Dialog(const char *title, const char *string)
+{
+}
+
+char *Sys_SDL_GetClipboardData (void)
+{
+       return NULL;
+}
+
+void Sys_SDL_Init(void)
+{
+}
+
+qbool sys_supportsdlgetticks = false;
+unsigned int Sys_SDL_GetTicks (void)
+{
+       Sys_Error("Called Sys_SDL_GetTicks on non-SDL target");
+       return 0;
+}
+void Sys_SDL_Delay (unsigned int milliseconds)
+{
+       Sys_Error("Called Sys_SDL_Delay on non-SDL target");
+}
+
+int main(int argc, char *argv[])
+{
+       return Sys_Main(argc, argv);
+}
index 1bf22cd08463c11279ddb3a70c93252a38994e9e..574f1355aacedb76cb10b57d497f29c910effe8b 100644 (file)
--- a/sys_sdl.c
+++ b/sys_sdl.c
@@ -1,18 +1,3 @@
-#ifdef WIN32
-#include <io.h> // Include this BEFORE darkplaces.h because it uses strncpy which trips DP_STATIC_ASSERT
-#include "conio.h"
-#else
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/time.h>
-#endif
-
-#ifdef __ANDROID__
-#include <android/log.h>
-#endif
-
-#include <signal.h>
-
 /*
  * Include this BEFORE darkplaces.h because it breaks wrapping
  * _Static_assert. Cloudwalk has no idea how or why so don't ask.
 #endif
 #endif
 
-sys_t sys;
 
 // =======================================================================
 // General routines
 // =======================================================================
 
-void Sys_Shutdown (void)
+void Sys_SDL_Shutdown(void)
 {
-#ifdef __ANDROID__
-       Sys_AllowProfiling(false);
-#endif
-#ifndef WIN32
-       fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~O_NONBLOCK);
-#endif
-       fflush(stdout);
        SDL_Quit();
 }
 
-static qbool nocrashdialog;
-void Sys_Error (const char *error, ...)
+// Sys_Error early in startup might screw with automated
+// workflows or something if we show the dialog by default.
+static qbool nocrashdialog = true;
+void Sys_SDL_Dialog(const char *title, const char *string)
 {
-       va_list argptr;
-       char string[MAX_INPUTLINE];
-
-// change stdin to non blocking
-#ifndef WIN32
-       fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~O_NONBLOCK);
-#endif
-
-       va_start (argptr,error);
-       dpvsnprintf (string, sizeof (string), error, argptr);
-       va_end (argptr);
-
-       Con_Printf(CON_ERROR "Engine Error: %s\n", string);
-       
        if(!nocrashdialog)
-               SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Engine Error", string, NULL);
-
-       //Host_Shutdown ();
-       exit (1);
-}
-
-void Sys_Print(const char *text)
-{
-#ifdef __ANDROID__
-       if (developer.integer > 0)
-       {
-               __android_log_write(ANDROID_LOG_DEBUG, sys.argv[0], text);
-       }
-#else
-       if(sys.outfd < 0)
-               return;
-#ifndef WIN32
-       // BUG: for some reason, NDELAY also affects stdout (1) when used on stdin (0).
-       // this is because both go to /dev/tty by default!
-       {
-               int origflags = fcntl (sys.outfd, F_GETFL, 0);
-               fcntl (sys.outfd, F_SETFL, origflags & ~O_NONBLOCK);
-#endif
-#ifdef WIN32
-#define write _write
-#endif
-               while(*text)
-               {
-                       fs_offset_t written = (fs_offset_t)write(sys.outfd, text, (int)strlen(text));
-                       if(written <= 0)
-                               break; // sorry, I cannot do anything about this error - without an output
-                       text += written;
-               }
-#ifndef WIN32
-               fcntl (sys.outfd, F_SETFL, origflags);
-       }
-#endif
-       //fprintf(stdout, "%s", text);
-#endif
+               SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, title, string, NULL);
 }
 
-char *Sys_ConsoleInput(void)
-{
-       static char text[MAX_INPUTLINE];
-       int len = 0;
-#ifdef WIN32
-       int c;
-
-       // read a line out
-       while (_kbhit ())
-       {
-               c = _getch ();
-               _putch (c);
-               if (c == '\r')
-               {
-                       text[len] = 0;
-                       _putch ('\n');
-                       len = 0;
-                       return text;
-               }
-               if (c == 8)
-               {
-                       if (len)
-                       {
-                               _putch (' ');
-                               _putch (c);
-                               len--;
-                               text[len] = 0;
-                       }
-                       continue;
-               }
-               text[len] = c;
-               len++;
-               text[len] = 0;
-               if (len == sizeof (text))
-                       len = 0;
-       }
-#else
-       fd_set fdset;
-       struct timeval timeout;
-       FD_ZERO(&fdset);
-       FD_SET(0, &fdset); // stdin
-       timeout.tv_sec = 0;
-       timeout.tv_usec = 0;
-       if (select (1, &fdset, NULL, NULL, &timeout) != -1 && FD_ISSET(0, &fdset))
-       {
-               len = read (0, text, sizeof(text));
-               if (len >= 1)
-               {
-                       // rip off the \n and terminate
-                       text[len-1] = 0;
-                       return text;
-               }
-       }
-#endif
-       return NULL;
-}
-
-char *Sys_GetClipboardData (void)
+char *Sys_SDL_GetClipboardData (void)
 {
        char *data = NULL;
        char *cliptext;
@@ -172,58 +42,22 @@ char *Sys_GetClipboardData (void)
                size_t allocsize;
                allocsize = min(MAX_INPUTLINE, strlen(cliptext) + 1);
                data = (char *)Z_Malloc (allocsize);
-               strlcpy (data, cliptext, allocsize);
+               dp_strlcpy (data, cliptext, allocsize);
                SDL_free(cliptext);
        }
 
        return data;
 }
 
-int main (int argc, char *argv[])
+void Sys_SDL_Init(void)
 {
-       signal(SIGFPE, SIG_IGN);
-
-#ifdef __ANDROID__
-       Sys_AllowProfiling(true);
-#endif
-
-       sys.selffd = -1;
-       sys.argc = argc;
-       sys.argv = (const char **)argv;
-
-       // Sys_Error this early in startup might screw with automated
-       // workflows or something if we show the dialog by default.
-       nocrashdialog = true;
-
-       Sys_ProvideSelfFD();
+       // we don't know which systems we'll want to init, yet...
+       if (SDL_Init(0) < 0)
+               Sys_Error("SDL_Init failed: %s\n", SDL_GetError());
 
-       // COMMANDLINEOPTION: -nocrashdialog disables "Engine Error" crash dialog boxes
+       // COMMANDLINEOPTION: sdl: -nocrashdialog disables "Engine Error" crash dialog boxes
        if(!Sys_CheckParm("-nocrashdialog"))
                nocrashdialog = false;
-       // COMMANDLINEOPTION: sdl: -noterminal disables console output on stdout
-       if(Sys_CheckParm("-noterminal"))
-               sys.outfd = -1;
-       // COMMANDLINEOPTION: sdl: -stderr moves console output to stderr
-       else if(Sys_CheckParm("-stderr"))
-               sys.outfd = 2;
-       else
-               sys.outfd = 1;
-
-#ifndef WIN32
-       fcntl(0, F_SETFL, fcntl (0, F_GETFL, 0) | O_NONBLOCK);
-#endif
-
-       // we don't know which systems we'll want to init, yet...
-       SDL_Init(0);
-
-       // used by everything
-       Memory_Init();
-
-       Host_Main();
-
-       Sys_Quit(0);
-       
-       return 0;
 }
 
 qbool sys_supportsdlgetticks = true;
@@ -235,3 +69,8 @@ void Sys_SDL_Delay (unsigned int milliseconds)
 {
        SDL_Delay(milliseconds);
 }
+
+int main(int argc, char *argv[])
+{
+       return Sys_Main(argc, argv);
+}
index 39c9bf8bf4e1507d37a0d59b831e078dae5c63a9..4222c42be414c88836cdfe48a87c73993482b554 100644 (file)
@@ -4,16 +4,14 @@
 # endif
 #endif
 
-#include "quakedef.h"
-#include "taskqueue.h"
-#include "thread.h"
-
 #define SUPPORTDLL
 
 #ifdef WIN32
 # include <windows.h>
 # include <mmsystem.h> // timeGetTime
 # include <time.h> // localtime
+# include <conio.h> // _kbhit, _getch, _putch
+# include <io.h> // write; Include this BEFORE darkplaces.h because it uses strncpy which trips DP_STATIC_ASSERT
 #ifdef _MSC_VER
 #pragma comment(lib, "winmm.lib")
 #endif
@@ -21,6 +19,9 @@
 # ifdef __FreeBSD__
 #  include <sys/sysctl.h>
 # endif
+# ifdef __ANDROID__
+#  include <android/log.h>
+# endif
 # include <unistd.h>
 # include <fcntl.h>
 # include <sys/time.h>
 # endif
 #endif
 
+#include <signal.h>
+
+#include "quakedef.h"
+#include "taskqueue.h"
+#include "thread.h"
+#include "libcurl.h"
+
+
+sys_t sys;
+
 static char sys_timestring[128];
 char *Sys_TimeString(const char *timeformat)
 {
@@ -45,21 +56,6 @@ char *Sys_TimeString(const char *timeformat)
 }
 
 
-void Sys_Quit (int returnvalue)
-{
-       // Unlock mutexes because the quit command may jump directly here, causing a deadlock
-       if ((cmd_local)->cbuf->lock)
-               Cbuf_Unlock((cmd_local)->cbuf);
-       SV_UnlockThreadMutex();
-       TaskQueue_Frame(true);
-
-       if (Sys_CheckParm("-profilegameonly"))
-               Sys_AllowProfiling(false);
-       host.state = host_shutdown;
-       Host_Shutdown();
-       exit(returnvalue);
-}
-
 #ifdef __cplusplus
 extern "C"
 #endif
@@ -186,13 +182,13 @@ notfound:
        if (!dllhandle && strrchr(sys.argv[0], '/'))
        {
                char path[MAX_OSPATH];
-               strlcpy(path, sys.argv[0], sizeof(path));
+               dp_strlcpy(path, sys.argv[0], sizeof(path));
                strrchr(path, '/')[1] = 0;
                for (i = 0; dllnames[i] != NULL; i++)
                {
                        char temp[MAX_OSPATH];
-                       strlcpy(temp, path, sizeof(temp));
-                       strlcat(temp, dllnames[i], sizeof(temp));
+                       dp_strlcpy(temp, path, sizeof(temp));
+                       dp_strlcat(temp, dllnames[i], sizeof(temp));
                        Con_DPrintf (" \"%s\"", temp);
 
                        if(Sys_LoadLibrary(temp, &dllhandle))
@@ -272,46 +268,41 @@ void* Sys_GetProcAddress (dllhandle_t handle, const char* name)
 #endif
 }
 
+
+
 #ifdef WIN32
 # define HAVE_TIMEGETTIME 1
 # define HAVE_QUERYPERFORMANCECOUNTER 1
+# define HAVE_WIN32_USLEEP 1
 # define HAVE_Sleep 1
-#endif
-
-#ifndef WIN32
-#if defined(CLOCK_MONOTONIC) || defined(CLOCK_HIRES)
-# define HAVE_CLOCKGETTIME 1
-#endif
-// FIXME improve this check, manpage hints to DST_NONE
-# define HAVE_GETTIMEOFDAY 1
-#endif
-
-#ifndef WIN32
-// on Win32, select() cannot be used with all three FD list args being NULL according to MSDN
-// (so much for POSIX...)
-# ifdef FD_SET
-#  define HAVE_SELECT 1
+#else
+# if defined(CLOCK_MONOTONIC) || defined(CLOCK_HIRES)
+#  define HAVE_CLOCKGETTIME 1
+# endif
+# if _POSIX_VERSION >= 200112L
+#  define HAVE_CLOCK_NANOSLEEP 1
 # endif
 #endif
 
-#ifndef WIN32
-// FIXME improve this check
-# define HAVE_USLEEP 1
+#ifdef FD_SET
+# define HAVE_SELECT 1
 #endif
 
 // these are referenced elsewhere
-cvar_t sys_usenoclockbutbenchmark = {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sys_usenoclockbutbenchmark", "0", "don't use ANY real timing, and simulate a clock (for benchmarking); the game then runs as fast as possible. Run a QC mod with bots that does some stuff, then does a quit at the end, to benchmark a server. NEVER do this on a public server."};
-cvar_t sys_libdir = {CF_READONLY | CF_CLIENT | CF_SERVER, "sys_libdir", "", "Default engine library directory"};
+cvar_t sys_usenoclockbutbenchmark = {CF_SHARED, "sys_usenoclockbutbenchmark", "0", "don't use ANY real timing, and simulate a clock (for benchmarking); the game then runs as fast as possible. Run a QC mod with bots that does some stuff, then does a quit at the end, to benchmark a server. NEVER do this on a public server."};
+cvar_t sys_libdir = {CF_READONLY | CF_SHARED, "sys_libdir", "", "Default engine library directory"};
 
 // these are not
-static cvar_t sys_debugsleep = {CF_CLIENT | CF_SERVER, "sys_debugsleep", "0", "write requested and attained sleep times to standard output, to be used with gnuplot"};
-static cvar_t sys_usesdlgetticks = {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sys_usesdlgetticks", "0", "use SDL_GetTicks() timer (less accurate, for debugging)"};
-static cvar_t sys_usesdldelay = {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sys_usesdldelay", "0", "use SDL_Delay() (less accurate, for debugging)"};
+static cvar_t sys_debugsleep = {CF_SHARED, "sys_debugsleep", "0", "write requested and attained sleep times to standard output, to be used with gnuplot"};
+static cvar_t sys_usesdlgetticks = {CF_SHARED, "sys_usesdlgetticks", "0", "use SDL_GetTicks() timer (low precision, for debugging)"};
+static cvar_t sys_usesdldelay = {CF_SHARED, "sys_usesdldelay", "0", "use SDL_Delay() (low precision, for debugging)"};
 #if HAVE_QUERYPERFORMANCECOUNTER
-static cvar_t sys_usequeryperformancecounter = {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sys_usequeryperformancecounter", "0", "use windows QueryPerformanceCounter timer (which has issues on multicore/multiprocessor machines and processors which are designed to conserve power) for timing rather than timeGetTime function (which has issues on some motherboards)"};
+static cvar_t sys_usequeryperformancecounter = {CF_SHARED | CF_ARCHIVE, "sys_usequeryperformancecounter", "1", "use windows QueryPerformanceCounter timer (which has issues on systems lacking constant-rate TSCs synchronised across all cores, such as ancient PCs or VMs) for timing rather than timeGetTime function (which is low precision and had issues on some old motherboards)"};
 #endif
-#if HAVE_CLOCKGETTIME
-static cvar_t sys_useclockgettime = {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sys_useclockgettime", "1", "use POSIX clock_gettime function (not adjusted by NTP on some older Linux kernels) for timing rather than gettimeofday (which has issues if the system time is stepped by ntpdate, or apparently on some Xen installations)"};
+
+static cvar_t sys_stdout = {CF_SHARED, "sys_stdout", "1", "0: nothing is written to stdout (-nostdout cmdline option sets this), 1: normal messages are written to stdout, 2: normal messages are written to stderr (-stderr cmdline option sets this)"};
+#ifndef WIN32
+static cvar_t sys_stdout_blocks = {CF_SHARED, "sys_stdout_blocks", "0", "1: writes to stdout and stderr streams will block (causing a stutter or complete halt) if the buffer is full, ensuring no messages are lost at a price"};
 #endif
 
 static double benchmark_time; // actually always contains an integer amount of milliseconds, will eventually "overflow"
@@ -339,12 +330,23 @@ int Sys_CheckParm (const char *parm)
        return 0;
 }
 
+static void Sys_UpdateOutFD_c(cvar_t *var)
+{
+       switch (sys_stdout.integer)
+       {
+               case 0: sys.outfd = -1; break;
+               default:
+               case 1: sys.outfd = fileno(stdout); break;
+               case 2: sys.outfd = fileno(stderr); break;
+       }
+}
+
 void Sys_Init_Commands (void)
 {
        Cvar_RegisterVariable(&sys_debugsleep);
        Cvar_RegisterVariable(&sys_usenoclockbutbenchmark);
        Cvar_RegisterVariable(&sys_libdir);
-#if HAVE_TIMEGETTIME || HAVE_QUERYPERFORMANCECOUNTER || HAVE_CLOCKGETTIME || HAVE_GETTIMEOFDAY
+#if HAVE_TIMEGETTIME || HAVE_QUERYPERFORMANCECOUNTER || HAVE_CLOCKGETTIME
        if(sys_supportsdlgetticks)
        {
                Cvar_RegisterVariable(&sys_usesdlgetticks);
@@ -354,11 +356,55 @@ void Sys_Init_Commands (void)
 #if HAVE_QUERYPERFORMANCECOUNTER
        Cvar_RegisterVariable(&sys_usequeryperformancecounter);
 #endif
-#if HAVE_CLOCKGETTIME
-       Cvar_RegisterVariable(&sys_useclockgettime);
+
+       Cvar_RegisterVariable(&sys_stdout);
+       Cvar_RegisterCallback(&sys_stdout, Sys_UpdateOutFD_c);
+#ifndef WIN32
+       Cvar_RegisterVariable(&sys_stdout_blocks);
 #endif
 }
 
+#ifdef WIN32
+static LARGE_INTEGER PerformanceFreq;
+/// Windows default timer resolution is only 15.625ms,
+/// this affects (at least) timeGetTime() and all forms of sleeping.
+static void Sys_SetTimerResolution(void)
+{
+       NTSTATUS(NTAPI *qNtQueryTimerResolution)(OUT PULONG MinRes, OUT PULONG MaxRes, OUT PULONG CurrentRes);
+       NTSTATUS(NTAPI *qNtSetTimerResolution)(IN ULONG DesiredRes, IN BOOLEAN SetRes, OUT PULONG ActualRes);
+       const char* ntdll_names [] =
+       {
+               "ntdll.dll",
+               NULL
+       };
+       dllfunction_t ntdll_funcs[] =
+       {
+               {"NtQueryTimerResolution", (void **) &qNtQueryTimerResolution},
+               {"NtSetTimerResolution",   (void **) &qNtSetTimerResolution},
+               {NULL, NULL}
+       };
+       dllhandle_t ntdll;
+       unsigned long WorstRes, BestRes, CurrentRes;
+
+       timeBeginPeriod(1); // 1ms, documented
+
+       // the best Windows can manage (typically 0.5ms)
+       // http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FTime%2FNtSetTimerResolution.html
+       if (Sys_LoadDependency(ntdll_names, &ntdll, ntdll_funcs))
+       {
+               qNtQueryTimerResolution(&WorstRes, &BestRes, &CurrentRes); // no pointers may be NULL
+               if (CurrentRes > BestRes)
+                       qNtSetTimerResolution(BestRes, true, &CurrentRes);
+
+               Sys_FreeLibrary(&ntdll);
+       }
+
+       // Microsoft says the freq is fixed at boot and consistent across all processors
+       // and that it need only be queried once and cached.
+       QueryPerformanceFrequency (&PerformanceFreq);
+}
+#endif // WIN32
+
 double Sys_DirtyTime(void)
 {
        // first all the OPTIONAL timers
@@ -372,28 +418,29 @@ double Sys_DirtyTime(void)
                        Sys_Error("sys_usenoclockbutbenchmark cannot run any longer, sorry");
                return benchmark_time * 0.000001;
        }
+
+       if(sys_supportsdlgetticks && sys_usesdlgetticks.integer)
+               return (double) Sys_SDL_GetTicks() / 1000.0;
+
 #if HAVE_QUERYPERFORMANCECOUNTER
        if (sys_usequeryperformancecounter.integer)
        {
-               // LadyHavoc: note to people modifying this code, DWORD is specifically defined as an unsigned 32bit number, therefore the 65536.0 * 65536.0 is fine.
                // QueryPerformanceCounter
                // platform:
                // Windows 95/98/ME/NT/2000/XP
                // features:
-               // very accurate (CPU cycles)
+               // + very accurate (constant-rate TSCs on modern systems)
                // known issues:
-               // does not necessarily match realtime too well (tends to get faster and faster in win98)
-               // wraps around occasionally on some platforms (depends on CPU speed and probably other unknown factors)
-               double timescale;
-               LARGE_INTEGER PerformanceFreq;
+               // does not necessarily match realtime too well (tends to get faster and faster in win98)
+               // wraps around occasionally on some platforms (depends on CPU speed and probably other unknown factors)
+               // - higher access latency on Vista
+               // Microsoft says on Win 7 or later, latency and overhead are very low, synchronisation is excellent.
                LARGE_INTEGER PerformanceCount;
 
-               if (QueryPerformanceFrequency (&PerformanceFreq))
+               if (PerformanceFreq.QuadPart)
                {
                        QueryPerformanceCounter (&PerformanceCount);
-       
-                       timescale = 1.0 / ((double) PerformanceFreq.LowPart + (double) PerformanceFreq.HighPart * 65536.0 * 65536.0);
-                       return ((double) PerformanceCount.LowPart + (double) PerformanceCount.HighPart * 65536.0 * 65536.0) * timescale;
+                       return (double)PerformanceCount.QuadPart * (1.0 / (double)PerformanceFreq.QuadPart);
                }
                else
                {
@@ -405,11 +452,13 @@ double Sys_DirtyTime(void)
 #endif
 
 #if HAVE_CLOCKGETTIME
-       if (sys_useclockgettime.integer)
        {
                struct timespec ts;
-#  ifdef CLOCK_MONOTONIC
-               // linux
+#  ifdef CLOCK_MONOTONIC_RAW
+               // Linux-specific, SDL_GetPerformanceCounter() uses it
+               clock_gettime(CLOCK_MONOTONIC, &ts);
+#  elif defined(CLOCK_MONOTONIC)
+               // POSIX
                clock_gettime(CLOCK_MONOTONIC, &ts);
 #  else
                // sunos
@@ -420,17 +469,8 @@ double Sys_DirtyTime(void)
 #endif
 
        // now all the FALLBACK timers
-       if(sys_supportsdlgetticks && sys_usesdlgetticks.integer)
-               return (double) Sys_SDL_GetTicks() / 1000.0;
-#if HAVE_GETTIMEOFDAY
-       {
-               struct timeval tp;
-               gettimeofday(&tp, NULL);
-               return (double) tp.tv_sec + tp.tv_usec / 1000000.0;
-       }
-#elif HAVE_TIMEGETTIME
+#if HAVE_TIMEGETTIME
        {
-               static int firsttimegettime = true;
                // timeGetTime
                // platform:
                // Windows 95/98/ME/NT/2000/XP
@@ -438,14 +478,7 @@ double Sys_DirtyTime(void)
                // reasonable accuracy (millisecond)
                // issues:
                // wraps around every 47 days or so (but this is non-fatal to us, odd times are rejected, only causes a one frame stutter)
-
-               // make sure the timer is high precision, otherwise different versions of windows have varying accuracy
-               if (firsttimegettime)
-               {
-                       timeBeginPeriod(1);
-                       firsttimegettime = false;
-               }
-
+               // requires Sys_SetTimerResolution()
                return (double) timeGetTime() / 1000.0;
        }
 #else
@@ -455,69 +488,267 @@ double Sys_DirtyTime(void)
 #endif
 }
 
-void Sys_Sleep(int microseconds)
+double Sys_Sleep(double time)
 {
-       double t = 0;
+       double dt;
+       uint32_t msec, usec, nsec;
+
+       if (time < 1.0/1000000.0 || host.restless)
+               return 0; // not sleeping this frame
+       if (time >= 1)
+               time = 0.999999; // ensure passed values are in range
+       msec = time * 1000;
+       usec = time * 1000000;
+       nsec = time * 1000000000;
+
        if(sys_usenoclockbutbenchmark.integer)
        {
-               if(microseconds)
-               {
-                       double old_benchmark_time = benchmark_time;
-                       benchmark_time += microseconds;
-                       if(benchmark_time == old_benchmark_time)
-                               Sys_Error("sys_usenoclockbutbenchmark cannot run any longer, sorry");
-               }
-               return;
+               double old_benchmark_time = benchmark_time;
+               benchmark_time += usec;
+               if(benchmark_time == old_benchmark_time)
+                       Sys_Error("sys_usenoclockbutbenchmark cannot run any longer, sorry");
+               return 0;
        }
+
        if(sys_debugsleep.integer)
+               Con_Printf("sys_debugsleep: requesting %u ", usec);
+       dt = Sys_DirtyTime();
+
+       // less important on newer libcurl so no need to disturb dedicated servers
+       if (cls.state != ca_dedicated && Curl_Select(msec))
        {
-               t = Sys_DirtyTime();
-       }
-       if(sys_supportsdlgetticks && sys_usesdldelay.integer)
-       {
-               Sys_SDL_Delay(microseconds / 1000);
+               // a transfer is ready or we finished sleeping
        }
+       else if(sys_supportsdlgetticks && sys_usesdldelay.integer)
+               Sys_SDL_Delay(msec);
 #if HAVE_SELECT
-       else
+       else if (cls.state == ca_dedicated && sv_checkforpacketsduringsleep.integer)
        {
                struct timeval tv;
-               tv.tv_sec = microseconds / 1000000;
-               tv.tv_usec = microseconds % 1000000;
-               select(0, NULL, NULL, NULL, &tv);
+               lhnetsocket_t *s;
+               fd_set fdreadset;
+               int lastfd = -1;
+
+               FD_ZERO(&fdreadset);
+               List_For_Each_Entry(s, &lhnet_socketlist.list, lhnetsocket_t, list)
+               {
+                       if (s->address.addresstype == LHNETADDRESSTYPE_INET4 || s->address.addresstype == LHNETADDRESSTYPE_INET6)
+                       {
+                               if (lastfd < s->inetsocket)
+                                       lastfd = s->inetsocket;
+       #if defined(WIN32) && !defined(_MSC_VER)
+                               FD_SET((int)s->inetsocket, &fdreadset);
+       #else
+                               FD_SET((unsigned int)s->inetsocket, &fdreadset);
+       #endif
+                       }
+               }
+               tv.tv_sec = 0;
+               tv.tv_usec = usec;
+               // on Win32, select() cannot be used with all three FD list args being NULL according to MSDN
+               // (so much for POSIX...), not with an empty fd_set either.
+               select(lastfd + 1, &fdreadset, NULL, NULL, &tv);
        }
-#elif HAVE_USLEEP
+#endif
+#if HAVE_CLOCK_NANOSLEEP
        else
        {
-               usleep(microseconds);
+               struct timespec ts;
+               ts.tv_sec = 0;
+               ts.tv_nsec = nsec;
+               clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL);
        }
-#elif HAVE_Sleep
+#elif HAVE_WIN32_USLEEP // Windows XP/2003 minimum
        else
        {
-               Sleep(microseconds / 1000);
+               HANDLE timer;
+               LARGE_INTEGER sleeptime;
+
+               // takes 100ns units, negative indicates relative time
+               sleeptime.QuadPart = -((int64_t)nsec / 100);
+               timer = CreateWaitableTimer(NULL, true, NULL);
+               SetWaitableTimer(timer, &sleeptime, 0, NULL, NULL, 0);
+               WaitForSingleObject(timer, INFINITE);
+               CloseHandle(timer);
        }
+#elif HAVE_Sleep
+       else
+               Sleep(msec);
 #else
        else
-       {
-               Sys_SDL_Delay(microseconds / 1000);
-       }
+               Sys_SDL_Delay(msec);
 #endif
+
+       dt = Sys_DirtyTime() - dt;
        if(sys_debugsleep.integer)
+               Con_Printf(" got %u oversleep %d\n", (unsigned int)(dt * 1000000), (unsigned int)(dt * 1000000) - usec);
+       return (dt < 0 || dt >= 1800) ? 0 : dt;
+}
+
+
+/*
+===============================================================================
+
+STDIO
+
+===============================================================================
+*/
+
+// NOTE: use only POSIX async-signal-safe library functions here (see: man signal-safety)
+void Sys_Print(const char *text, size_t textlen)
+{
+#ifdef __ANDROID__
+       if (developer.integer > 0)
        {
-               t = Sys_DirtyTime() - t;
-               Sys_Printf("%d %d # debugsleep\n", microseconds, (unsigned int)(t * 1000000));
+               __android_log_write(ANDROID_LOG_DEBUG, sys.argv[0], text);
        }
+#else
+       if(sys.outfd < 0)
+               return;
+  #ifndef WIN32
+       // BUG: for some reason, NDELAY also affects stdout (1) when used on stdin (0).
+       // this is because both go to /dev/tty by default!
+       {
+               int origflags = fcntl(sys.outfd, F_GETFL, 0);
+               if (sys_stdout_blocks.integer)
+                       fcntl(sys.outfd, F_SETFL, origflags & ~O_NONBLOCK);
+  #else
+    #define write _write
+  #endif
+               while(*text)
+               {
+                       fs_offset_t written = (fs_offset_t)write(sys.outfd, text, textlen);
+                       if(written <= 0)
+                               break; // sorry, I cannot do anything about this error - without an output
+                       text += written;
+               }
+  #ifndef WIN32
+               if (sys_stdout_blocks.integer)
+                       fcntl(sys.outfd, F_SETFL, origflags);
+       }
+  #endif
+       //fprintf(stdout, "%s", text);
+#endif
 }
 
 void Sys_Printf(const char *fmt, ...)
 {
        va_list argptr;
        char msg[MAX_INPUTLINE];
+       int msglen;
 
        va_start(argptr,fmt);
-       dpvsnprintf(msg,sizeof(msg),fmt,argptr);
+       msglen = dpvsnprintf(msg, sizeof(msg), fmt, argptr);
        va_end(argptr);
 
-       Sys_Print(msg);
+       if (msglen >= 0)
+               Sys_Print(msg, msglen);
+}
+
+/// Reads a line from POSIX stdin or the Windows console
+char *Sys_ConsoleInput(void)
+{
+       static char text[MAX_INPUTLINE];
+#ifdef WIN32
+       static unsigned int len = 0;
+       int c;
+
+       // read a line out
+       while (_kbhit ())
+       {
+               c = _getch ();
+               if (c == '\r')
+               {
+                       text[len] = '\0';
+                       _putch ('\n');
+                       len = 0;
+                       return text;
+               }
+               if (c == '\b')
+               {
+                       if (len)
+                       {
+                               _putch (c);
+                               _putch (' ');
+                               _putch (c);
+                               len--;
+                       }
+                       continue;
+               }
+               if (len < sizeof (text) - 1)
+               {
+                       _putch (c);
+                       text[len] = c;
+                       len++;
+               }
+       }
+#else
+       fd_set fdset;
+       struct timeval timeout = { .tv_sec = 0, .tv_usec = 0 };
+
+       FD_ZERO(&fdset);
+       FD_SET(fileno(stdin), &fdset);
+       if (select(1, &fdset, NULL, NULL, &timeout) != -1 && FD_ISSET(fileno(stdin), &fdset))
+               return fgets(text, sizeof(text), stdin);
+#endif
+       return NULL;
+}
+
+
+/*
+===============================================================================
+
+Startup and Shutdown
+
+===============================================================================
+*/
+
+void Sys_Error (const char *error, ...)
+{
+       va_list argptr;
+       char string[MAX_INPUTLINE];
+       int i;
+
+       // Disable Sys_HandleSignal() but not Sys_HandleCrash()
+       host.state = host_shutdown;
+
+       // set output to blocking stderr
+       sys.outfd = fileno(stderr);
+#ifndef WIN32
+       fcntl(sys.outfd, F_SETFL, fcntl(sys.outfd, F_GETFL, 0) & ~O_NONBLOCK);
+#endif
+
+       va_start (argptr,error);
+       dpvsnprintf (string, sizeof (string), error, argptr);
+       va_end (argptr);
+
+       Con_Printf(CON_ERROR "Engine Aborted: %s\n^9%s\n", string, engineversion);
+
+       dp_strlcat(string, "\n\n", sizeof(string));
+       dp_strlcat(string, engineversion, sizeof(string));
+
+       // Most shutdown funcs can't be called here as they could error while we error.
+
+       // DP8 TODO: send a disconnect message indicating we aborted, see Host_Error() and Sys_HandleCrash()
+
+       if (cls.demorecording)
+               CL_Stop_f(cmd_local);
+       if (sv.active)
+       {
+               sv.active = false; // make SV_DropClient() skip the QC stuff to avoid recursive errors
+               for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
+                       if (host_client->active)
+                               SV_DropClient(false, "Server aborted!"); // closes demo file
+       }
+       // don't want a dead window left blocking the OS UI or the abort dialog
+       VID_Shutdown();
+       S_StopAllSounds();
+
+       host.state = host_failed; // make Sys_HandleSignal() call _Exit()
+       Sys_SDL_Dialog("Engine Aborted", string);
+
+       fflush(stderr);
+       exit (1);
 }
 
 #ifndef WIN32
@@ -713,3 +944,200 @@ void Sys_MakeProcessMean (void)
 {
 }
 #endif
+
+
+static const char *Sys_SigDesc(int sig)
+{
+       switch (sig)
+       {
+               // Windows only supports the C99 signals
+               case SIGINT:  return "Interrupt";
+               case SIGILL:  return "Illegal instruction";
+               case SIGABRT: return "Aborted";
+               case SIGFPE:  return "Floating point exception";
+               case SIGSEGV: return "Segmentation fault";
+               case SIGTERM: return "Termination";
+#ifndef WIN32
+               // POSIX has several others worth catching
+               case SIGHUP:  return "Hangup";
+               case SIGQUIT: return "Quit";
+               case SIGBUS:  return "Bus error (bad memory access)";
+               case SIGPIPE: return "Broken pipe";
+#endif
+               default:      return "Yo dawg, we bugged out while bugging out";
+       }
+}
+
+/** Halt and try not to catch fire.
+ * Writing to any file could corrupt it,
+ * any uneccessary code could crash while we crash.
+ * Try to use only POSIX async-signal-safe library functions here (see: man signal-safety).
+ */
+static void Sys_HandleCrash(int sig)
+{
+#if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1
+       // Before doing anything else grab the stack frame addresses
+       #include <execinfo.h>
+       void *stackframes[32];
+       int framecount = backtrace(stackframes, 32);
+       char **btstrings;
+#endif
+       char dialogtext[3072];
+       const char *sigdesc;
+
+       // Break any loop and disable Sys_HandleSignal()
+       if (host.state == host_failing || host.state == host_failed)
+               return;
+       host.state = host_failing;
+
+       sigdesc = Sys_SigDesc(sig);
+
+       // set output to blocking stderr and print header, backtrace, version
+       sys.outfd = fileno(stderr); // not async-signal-safe :(
+#ifndef WIN32
+       fcntl(sys.outfd, F_SETFL, fcntl(sys.outfd, F_GETFL, 0) & ~O_NONBLOCK);
+       Sys_Print("\n\n\e[1;37;41m    Engine Crash: ", 30);
+       Sys_Print(sigdesc, strlen(sigdesc));
+       Sys_Print("    \e[m\n", 8);
+  #if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1
+       // the first two addresses will be in this function and in signal() in libc
+       backtrace_symbols_fd(stackframes + 2, framecount - 2, sys.outfd);
+  #endif
+       Sys_Print("\e[1m", 4);
+       Sys_Print(engineversion, strlen(engineversion));
+       Sys_Print("\e[m\n", 4);
+#else // Windows console doesn't support colours
+       Sys_Print("\n\nEngine Crash: ", 16);
+       Sys_Print(sigdesc, strlen(sigdesc));
+       Sys_Print("\n", 1);
+       Sys_Print(engineversion, strlen(engineversion));
+       Sys_Print("\n", 1);
+#endif
+
+       // DP8 TODO: send a disconnect message indicating we crashed, see Sys_Error() and Host_Error()
+
+       // don't want a dead window left blocking the OS UI or the crash dialog
+       VID_Shutdown();
+       S_StopAllSounds();
+
+       // prepare the dialogtext: signal, backtrace, version
+       // the dp_st* funcs are POSIX async-signal-safe IF we don't trigger their warnings
+       dp_strlcpy(dialogtext, sigdesc, sizeof(dialogtext));
+       dp_strlcat(dialogtext, "\n\n", sizeof(dialogtext));
+#if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1
+       btstrings = backtrace_symbols(stackframes + 2, framecount - 2); // calls malloc :(
+       if (btstrings)
+               for (int i = 0; i < framecount - 2; ++i)
+               {
+                       dp_strlcat(dialogtext, btstrings[i], sizeof(dialogtext));
+                       dp_strlcat(dialogtext, "\n", sizeof(dialogtext));
+               }
+#endif
+       dp_strlcat(dialogtext, "\n", sizeof(dialogtext));
+       dp_strlcat(dialogtext, engineversion, sizeof(dialogtext));
+
+       host.state = host_failed; // make Sys_HandleSignal() call _Exit()
+       Sys_SDL_Dialog("Engine Crash", dialogtext);
+
+       fflush(stderr); // not async-signal-safe :(
+
+       // Continue execution with default signal handling.
+       // A real crash will be re-triggered so the platform can handle it,
+       // a fake crash (kill -SEGV) will cause a graceful shutdown.
+       signal(sig, SIG_DFL);
+}
+
+static void Sys_HandleSignal(int sig)
+{
+       const char *sigdesc;
+
+       // Break any loop, eg if each Sys_Print triggers a SIGPIPE
+       if (host.state == host_shutdown || host.state == host_failing)
+               return;
+
+       sigdesc = Sys_SigDesc(sig);
+       Sys_Print("\nReceived ", 10);
+       Sys_Print(sigdesc, strlen(sigdesc));
+       Sys_Print(" signal, exiting...\n", 20);
+       if (host.state == host_failed)
+       {
+               // user is trying to kill the process while the SDL dialog is open
+               fflush(stderr); // not async-signal-safe :(
+               _Exit(sig);
+       }
+       host.state = host_shutdown;
+}
+
+/// SDL2 only handles SIGINT and SIGTERM by default and doesn't log anything
+static void Sys_InitSignals(void)
+{
+       // Windows only supports the C99 signals
+       signal(SIGINT,  Sys_HandleSignal);
+       signal(SIGILL,  Sys_HandleCrash);
+       signal(SIGABRT, Sys_HandleCrash);
+       signal(SIGFPE,  Sys_HandleCrash);
+       signal(SIGSEGV, Sys_HandleCrash);
+       signal(SIGTERM, Sys_HandleSignal);
+#ifndef WIN32
+       // POSIX has several others worth catching
+       signal(SIGHUP,  Sys_HandleSignal);
+       signal(SIGQUIT, Sys_HandleSignal);
+       signal(SIGBUS,  Sys_HandleCrash);
+       signal(SIGPIPE, Sys_HandleSignal);
+#endif
+}
+
+/** main() but renamed so we can wrap it in sys_sdl.c and sys_null.c
+ * to avoid needing to include SDL.h in this file (would make the dedicated server require SDL).
+ * SDL builds need SDL.h in the file where main() is defined because SDL renames and wraps main().
+ */
+int Sys_Main(int argc, char *argv[])
+{
+       sys.argc = argc;
+       sys.argv = (const char **)argv;
+
+       // COMMANDLINEOPTION: Console: -nostdout disables text output to the terminal the game was launched from
+       // COMMANDLINEOPTION: -noterminal disables console output on stdout
+       if(Sys_CheckParm("-noterminal") || Sys_CheckParm("-nostdout"))
+               sys_stdout.string = "0";
+       // COMMANDLINEOPTION: -stderr moves console output to stderr
+       else if(Sys_CheckParm("-stderr"))
+               sys_stdout.string = "2";
+       // too early for Cvar_SetQuick
+       sys_stdout.value = sys_stdout.integer = atoi(sys_stdout.string);
+       Sys_UpdateOutFD_c(&sys_stdout);
+#ifndef WIN32
+       fcntl(fileno(stdin), F_SETFL, fcntl(fileno(stdin), F_GETFL, 0) | O_NONBLOCK);
+       // stdout/stderr will be set to blocking in Sys_Print() if so configured, or during a fatal error.
+       fcntl(fileno(stdout), F_SETFL, fcntl(fileno(stdout), F_GETFL, 0) | O_NONBLOCK);
+       fcntl(fileno(stderr), F_SETFL, fcntl(fileno(stderr), F_GETFL, 0) | O_NONBLOCK);
+#endif
+
+       sys.selffd = -1;
+       Sys_ProvideSelfFD(); // may call Con_Printf() so must be after sys.outfd is set
+
+#ifdef __ANDROID__
+       Sys_AllowProfiling(true);
+#endif
+
+       Sys_InitSignals();
+
+#ifdef WIN32
+       Sys_SetTimerResolution();
+#endif
+
+       Host_Main();
+
+#ifdef __ANDROID__
+       Sys_AllowProfiling(false);
+#endif
+
+#ifndef WIN32
+       fcntl(fileno(stdout), F_SETFL, fcntl(fileno(stdout), F_GETFL, 0) & ~O_NONBLOCK);
+       fcntl(fileno(stderr), F_SETFL, fcntl(fileno(stderr), F_GETFL, 0) & ~O_NONBLOCK);
+#endif
+       fflush(stdout);
+       fflush(stderr);
+
+       return 0;
+}
diff --git a/sys_unix.c b/sys_unix.c
deleted file mode 100644 (file)
index 2ff100d..0000000
+++ /dev/null
@@ -1,180 +0,0 @@
-
-#ifdef WIN32
-#include <windows.h>
-#include <mmsystem.h>
-#include <io.h>
-#include "conio.h"
-#else
-#include <sys/time.h>
-#include <unistd.h>
-#include <fcntl.h>
-#endif
-
-#include <signal.h>
-
-#include "darkplaces.h"
-
-sys_t sys;
-
-// =======================================================================
-// General routines
-// =======================================================================
-void Sys_Shutdown (void)
-{
-#ifndef WIN32
-       fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~O_NONBLOCK);
-#endif
-       fflush(stdout);
-}
-
-void Sys_Error (const char *error, ...)
-{
-       va_list argptr;
-       char string[MAX_INPUTLINE];
-
-// change stdin to non blocking
-#ifndef WIN32
-       fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~O_NONBLOCK);
-#endif
-       va_start (argptr,error);
-       dpvsnprintf (string, sizeof (string), error, argptr);
-       va_end (argptr);
-
-       Con_Printf(CON_ERROR "Engine Error: %s\n", string);
-
-       //Host_Shutdown ();
-       exit (1);
-}
-
-void Sys_Print(const char *text)
-{
-       if(sys.outfd < 0)
-               return;
-       // BUG: for some reason, NDELAY also affects stdout (1) when used on stdin (0).
-       // this is because both go to /dev/tty by default!
-       {
-#ifndef WIN32
-               int origflags = fcntl (sys.outfd, F_GETFL, 0);
-               fcntl (sys.outfd, F_SETFL, origflags & ~O_NONBLOCK);
-#else
-#define write _write
-#endif
-               while(*text)
-               {
-                       fs_offset_t written = (fs_offset_t)write(sys.outfd, text, (int)strlen(text));
-                       if(written <= 0)
-                               break; // sorry, I cannot do anything about this error - without an output
-                       text += written;
-               }
-#ifndef WIN32
-               fcntl (sys.outfd, F_SETFL, origflags);
-#endif
-       }
-       //fprintf(stdout, "%s", text);
-}
-
-char *Sys_ConsoleInput(void)
-{
-       static char text[MAX_INPUTLINE];
-       static unsigned int len = 0;
-#ifdef WIN32
-       int c;
-
-       // read a line out
-       while (_kbhit ())
-       {
-               c = _getch ();
-               if (c == '\r')
-               {
-                       text[len] = '\0';
-                       _putch ('\n');
-                       len = 0;
-                       return text;
-               }
-               if (c == '\b')
-               {
-                       if (len)
-                       {
-                               _putch (c);
-                               _putch (' ');
-                               _putch (c);
-                               len--;
-                       }
-                       continue;
-               }
-               if (len < sizeof (text) - 1)
-               {
-                       _putch (c);
-                       text[len] = c;
-                       len++;
-               }
-       }
-#else
-       fd_set fdset;
-       struct timeval timeout;
-       FD_ZERO(&fdset);
-       FD_SET(0, &fdset); // stdin
-       timeout.tv_sec = 0;
-       timeout.tv_usec = 0;
-       if (select (1, &fdset, NULL, NULL, &timeout) != -1 && FD_ISSET(0, &fdset))
-       {
-               len = read (0, text, sizeof(text) - 1);
-               if (len >= 1)
-               {
-                       // rip off the \n and terminate
-                       // div0: WHY? console code can deal with \n just fine
-                       // this caused problems with pasting stuff into a terminal window
-                       // so, not ripping off the \n, but STILL keeping a NUL terminator
-                       text[len] = 0;
-                       return text;
-               }
-       }
-#endif
-       return NULL;
-}
-
-char *Sys_GetClipboardData (void)
-{
-       return NULL;
-}
-
-int main (int argc, char **argv)
-{
-       signal(SIGFPE, SIG_IGN);
-       sys.selffd = -1;
-       sys.argc = argc;
-       sys.argv = (const char **)argv;
-       Sys_ProvideSelfFD();
-
-       // COMMANDLINEOPTION: sdl: -noterminal disables console output on stdout
-       if(Sys_CheckParm("-noterminal"))
-               sys.outfd = -1;
-       // COMMANDLINEOPTION: sdl: -stderr moves console output to stderr
-       else if(Sys_CheckParm("-stderr"))
-               sys.outfd = 2;
-       else
-               sys.outfd = 1;
-#ifndef WIN32
-       fcntl(0, F_SETFL, fcntl (0, F_GETFL, 0) | O_NONBLOCK);
-#endif
-
-       // used by everything
-       Memory_Init();
-
-       Host_Main();
-
-       Sys_Quit(0);
-
-       return 0;
-}
-
-qbool sys_supportsdlgetticks = false;
-unsigned int Sys_SDL_GetTicks (void)
-{
-       Sys_Error("Called Sys_SDL_GetTicks on non-SDL target");
-       return 0;
-}
-void Sys_SDL_Delay (unsigned int milliseconds)
-{
-       Sys_Error("Called Sys_SDL_Delay on non-SDL target");
-}
index e2445d43cb621dad52c831fd71576ad27656478e..175a28af7c21c74aea3562ea4423b99c4c701a24 100644 (file)
@@ -96,7 +96,7 @@ static int TaskQueue_ThreadFunc(void *d)
                        break;
                sleepcounter++;
                if (sleepcounter >= THREADSLEEPCOUNT)
-                       Sys_Sleep(1000);
+                       Sys_Sleep(0.001);
                sleepcounter = 0;
        }
        return 0;
diff --git a/todo b/todo
index 28e8cb6c475a41239aab785fcf76bae8db5a6a35..19c25c34b47e9d650b9b9ffda9b4c314e53dd80d 100644 (file)
--- a/todo
+++ b/todo
 0 optimize darkplaces renderer: get rid of attenuation texture on lights because math is faster, add fastpath for no normalmap (Lava_Croft)
 1 bug darkplaces WGL client: figure out why for some people GDI input has stuttering problems with gl_finish 0 mode (Kinn, Urre, romi, Spike, Black)
 1 bug darkplaces WGL/GLX/SDL client bug: if sound is unavailable (causing a freeze waiting for it to become available), the config is reset (SavageX)
-1 bug darkplaces bsd filesystem: read() is failing (not returning the requested amount) on freebsd when reading files, whether actual files or in a pk3 - somehow it is still able to read the pk3 zip directory though (suminigashi, Elric)
 1 bug darkplaces collisions: curve collisions sometimes catch on the lip of the edge, pushing into the curved back wall around certain jumppads in Nexuiz for example consistently gets stuck just below the ledge (HReaper)
 1 bug darkplaces command: "rate", "playermodel", "playerskin", "pmodel" commands can spam server console with usage statements (Spike)
 1 bug darkplaces console: when logging using log_file and log_sync 0, setting log_file back to "" does not close the file until another message is posted?
@@ -1356,6 +1355,7 @@ d revelation: fix lingering glow on lightning deaths (romi)
 d revelation: reduce damage from weapons (romi)
 d sv_user.qc: figure out why looking up/down slows movement and fix it (Vermeulen)
 d zmodel: fix scale and origin commands (Vermeulen)
+-d bug darkplaces bsd filesystem: read() is failing (not returning the requested amount) on freebsd when reading files, whether actual files or in a pk3 - somehow it is still able to read the pk3 zip directory though (suminigashi, Elric)
 f LordHavoc: examine .mb (Maya Binary) file from Electro and learn its format (Electro)
 f bug darkplaces capturevideo: cl_capturevideo 1 with sound off is not locking the framerate of a server (Vermeulen)
 f bug darkplaces client: decals are not sticking to submodels
index 155b4aea9a8f949ecf8e2a583487c4fa122896cc..1e34e707c978413cb8ef999db36a6e5dd24ecb8b 100644 (file)
--- a/utf8lib.c
+++ b/utf8lib.c
@@ -170,7 +170,7 @@ findchar:
             (bits >= 3 && ch < 0x800) ||
             (bits >= 4 && ch < 0x10000) ||
             ch >= 0x10FFFF // RFC 3629
-               )
+       )
        {
                i += bits;
                //fprintf(stderr, "overlong: %i bytes for %x\n", bits, ch);
@@ -907,6 +907,115 @@ size_t u8_strpad_colorcodes(char *out, size_t outsize, const char *in, qbool lef
        return dpsnprintf(out, outsize, "%*s%.*s%*s", lpad, "", prec, in, rpad, "");
 }
 
+#ifdef WIN32
+
+/**
+ * Convert Windows "wide" characters to WTF-8 for internal manipulation
+ */
+int towtf8(const wchar *wstr, int wlen, char *cstr, int maxclen)
+{
+       int p = 0;
+       int i;
+       for (i = 0; i < wlen; ++i)
+       {
+               wchar point = wstr[i];
+               if (point < 0x80)
+               {
+                       if (p + 1 >= maxclen) break;
+                       cstr[p++] = point;
+               }
+               else if (point < 0x800)
+               {
+                       if (p + 2 >= maxclen) break;
+                       cstr[p++] = (0xc0 | ((point >>  6) & 0x1f));
+                       cstr[p++] = (0x80 | ((point >>  0) & 0x3f));
+               }
+               else
+               #if WTF8U32
+               if (point < 0x10000)
+               #endif
+               {
+                       if (p + 3 >= maxclen) break;
+                       cstr[p++] = (0xe0 | ((point >> 12) & 0x0f));
+                       cstr[p++] = (0x80 | ((point >>  6) & 0x3f));
+                       cstr[p++] = (0x80 | ((point >>  0) & 0x3f));
+               }
+               #if WTF8U32
+               else
+               #if WTF8CHECKS
+               if (point < 0x110000)
+               #endif
+               {
+                       if (p + 4 >= maxclen) break;
+                       cstr[p++] = (0xf0 | ((point >> 18) & 0x07));
+                       cstr[p++] = (0x80 | ((point >> 12) & 0x3f));
+                       cstr[p++] = (0x80 | ((point >>  6) & 0x3f));
+                       cstr[p++] = (0x80 | ((point >>  0) & 0x3f));
+               }
+               #endif
+       }
+       cstr[p] = 0x00;
+       return p;
+}
+
+/**
+ * Convert WTF-8 string to "wide" characters used by Windows
+ */
+int fromwtf8(const char *cstr, int clen, wchar *wstr, int maxwlen)
+{
+       int p = 0;
+       int i;
+       for (i = 0; i < clen;)
+       {
+               char byte = cstr[i++];
+               wchar point = byte;
+               int length = 1;
+               if (p + 1 >= maxwlen) break;
+               #if WTF8CHECKS
+               if ((byte & 0xf8) == 0xf8)
+                       return -1;
+               #endif
+               #if WTF8U32
+               if ((byte & 0xf8) == 0xf0)
+               {
+                       length = 4;
+                       point = byte & 0x07;
+               }
+               else
+               #endif
+               if ((byte & 0xf0) == 0xe0)
+               {
+                       length = 3;
+                       point = byte & 0x0f;
+               }
+               else if ((byte & 0xe0) == 0xc0)
+               {
+                       length = 2;
+                       point = byte & 0x1f;
+               }
+               #if WTF8CHECKS
+               else if ((byte & 0xc0) == 0x80)
+               {
+                       return -1;
+               }
+               #endif
+               while (--length)
+               {
+                       byte = cstr[i++];
+                       #if WTF8CHECKS
+                       if (byte == -1) return -1;
+                       else if ((byte & 0xc0) != 0x80) return -1;
+                       #endif
+                       point = (point << 6) | (byte & 0x3f);
+               }
+               wstr[p++] = point;
+       }
+       wstr[p] = 0x00;
+       return p;
+}
+
+#endif // WIN32
+
 
 /*
 The two following functions (u8_toupper, u8_tolower) are derived from
index 4b8221f5f779da6d7ca6d9103c6a69173acb08fa..b50a20655c8ce3ce1790dd6a1f81b9dec85b0a3f 100644 (file)
--- a/utf8lib.h
+++ b/utf8lib.h
@@ -78,4 +78,17 @@ extern Uchar u8_quake2utf8map[256];
 Uchar u8_toupper(Uchar ch);
 Uchar u8_tolower(Uchar ch);
 
+#ifdef WIN32
+
+// WTF-8 encoding to circumvent Windows encodings, be it UTF-16 or random codepages
+// https://simonsapin.github.io/wtf-8/
+#define WTF8U32 0     // whether to regard wchar as utf-32
+#define WTF8CHECKS 1  // check for extra sanity in conversion steps
+typedef wchar_t wchar;
+
+int towtf8(const wchar* wstr, int wlen, char* cstr, int maxclen);
+int fromwtf8(const char* cstr, int clen, wchar* wstr, int maxwlen);
+
+#endif // WIN32
+
 #endif // UTF8LIB_H__
diff --git a/vid.h b/vid.h
index 8b8fa3f5da0e277ac010f4d053076d56ab08e232..3ede535d9238c7c179a87b12ea27024e4ab377b5 100644 (file)
--- a/vid.h
+++ b/vid.h
@@ -41,6 +41,7 @@ renderpath_t;
 
 typedef struct viddef_support_s
 {
+       int glversion; // this is at least 32
        int glshaderversion; // this is at least 150 (GL 3.2)
        qbool amd_texture_texture4;
        qbool arb_texture_gather;
@@ -95,6 +96,9 @@ typedef struct viddef_s
        viddef_support_t support;
 
        int forcetextype; // always use GL_BGRA for D3D, always use GL_RGBA for GLES, etc
+
+       int xPos, yPos; // current virtual position of the top left corner of the SDL window
+       unsigned char displayindex; // the monitor it's on currently
 } viddef_t;
 
 // global video state
@@ -115,10 +119,10 @@ vid_joystate_t;
 
 extern vid_joystate_t vid_joystate;
 
-extern struct cvar_s joy_index;
-extern struct cvar_s joy_enable;
-extern struct cvar_s joy_detected;
-extern struct cvar_s joy_active;
+extern cvar_t joy_index;
+extern cvar_t joy_enable;
+extern cvar_t joy_detected;
+extern cvar_t joy_active;
 
 float VID_JoyState_GetAxis(const vid_joystate_t *joystate, int axis, float sensitivity, float deadzone);
 void VID_ApplyJoyState(vid_joystate_t *joystate);
@@ -129,54 +133,66 @@ int VID_Shared_SetJoystick(int index);
 qbool VID_JoyBlockEmulatedKeys(int keycode);
 void VID_EnableJoystick(qbool enable);
 
+extern cvar_t cl_demo_mousegrab;
+extern qbool scr_loading;
+
 extern qbool vid_hidden;
 extern qbool vid_activewindow;
 extern qbool vid_supportrefreshrate;
 
-extern struct cvar_s vid_fullscreen;
-extern struct cvar_s vid_borderless;
-extern struct cvar_s vid_width;
-extern struct cvar_s vid_height;
-extern struct cvar_s vid_bitsperpixel;
-extern struct cvar_s vid_samples;
-extern struct cvar_s vid_refreshrate;
-extern struct cvar_s vid_userefreshrate;
-extern struct cvar_s vid_touchscreen_density;
-extern struct cvar_s vid_touchscreen_xdpi;
-extern struct cvar_s vid_touchscreen_ydpi;
-extern struct cvar_s vid_vsync;
-extern struct cvar_s vid_mouse;
-extern struct cvar_s vid_mouse_clickthrough;
-extern struct cvar_s vid_grabkeyboard;
-extern struct cvar_s vid_touchscreen;
-extern struct cvar_s vid_touchscreen_showkeyboard;
-extern struct cvar_s vid_touchscreen_supportshowkeyboard;
-extern struct cvar_s vid_stick_mouse;
-extern struct cvar_s vid_resizable;
-extern struct cvar_s vid_desktopfullscreen;
+extern cvar_t vid_fullscreen;
+extern cvar_t vid_borderless;
+extern cvar_t vid_width;
+extern cvar_t vid_height;
+extern cvar_t vid_bitsperpixel;
+extern cvar_t vid_samples;
+extern cvar_t vid_refreshrate;
+extern cvar_t vid_userefreshrate;
+extern cvar_t vid_touchscreen_density;
+extern cvar_t vid_touchscreen_xdpi;
+extern cvar_t vid_touchscreen_ydpi;
+extern cvar_t vid_vsync;
+extern cvar_t vid_mouse;
+extern cvar_t vid_mouse_clickthrough;
+extern cvar_t vid_minimize_on_focus_loss;
+extern cvar_t vid_grabkeyboard;
+extern cvar_t vid_touchscreen;
+extern cvar_t vid_touchscreen_showkeyboard;
+extern cvar_t vid_touchscreen_supportshowkeyboard;
+extern cvar_t vid_stick_mouse;
+extern cvar_t vid_resizable;
+extern cvar_t vid_desktopfullscreen;
+extern cvar_t vid_display;
+extern cvar_t vid_info_displaycount;
 #ifdef WIN32
-extern struct cvar_s vid_ignore_taskbar;
+extern cvar_t vid_ignore_taskbar;
 #endif
-extern struct cvar_s vid_minwidth;
-extern struct cvar_s vid_minheight;
-extern struct cvar_s vid_sRGB;
-extern struct cvar_s vid_sRGB_fallback;
-
-extern struct cvar_s gl_finish;
-
-extern struct cvar_s v_gamma;
-extern struct cvar_s v_contrast;
-extern struct cvar_s v_brightness;
-extern struct cvar_s v_color_enable;
-extern struct cvar_s v_color_black_r;
-extern struct cvar_s v_color_black_g;
-extern struct cvar_s v_color_black_b;
-extern struct cvar_s v_color_grey_r;
-extern struct cvar_s v_color_grey_g;
-extern struct cvar_s v_color_grey_b;
-extern struct cvar_s v_color_white_r;
-extern struct cvar_s v_color_white_g;
-extern struct cvar_s v_color_white_b;
+extern cvar_t vid_minwidth;
+extern cvar_t vid_minheight;
+extern cvar_t vid_sRGB;
+extern cvar_t vid_sRGB_fallback;
+
+extern cvar_t gl_finish;
+
+extern cvar_t v_gamma;
+extern cvar_t v_contrast;
+extern cvar_t v_brightness;
+extern cvar_t v_color_enable;
+extern cvar_t v_color_black_r;
+extern cvar_t v_color_black_g;
+extern cvar_t v_color_black_b;
+extern cvar_t v_color_grey_r;
+extern cvar_t v_color_grey_g;
+extern cvar_t v_color_grey_b;
+extern cvar_t v_color_white_r;
+extern cvar_t v_color_white_g;
+extern cvar_t v_color_white_b;
+
+extern cvar_t gl_info_vendor;
+extern cvar_t gl_info_renderer;
+extern cvar_t gl_info_version;
+extern cvar_t gl_info_extensions;
+extern cvar_t gl_info_driver;
 
 // brand of graphics chip
 extern const char *gl_vendor;
@@ -184,12 +200,7 @@ extern const char *gl_vendor;
 extern const char *gl_renderer;
 // begins with 1.0.0, 1.1.0, 1.2.0, 1.2.1, 1.3.0, 1.3.1, or 1.4.0
 extern const char *gl_version;
-// extensions list, space separated
-extern const char *gl_extensions;
-// WGL, GLX, or AGL
-extern const char *gl_platform;
-// name of driver library (opengl32.dll, libGL.so.1, or whatever)
-extern char gl_driver[256];
+
 
 void *GL_GetProcAddress(const char *name);
 qbool GL_CheckExtension(const char *name, const char *disableparm, int silent);
@@ -197,6 +208,7 @@ qbool GL_ExtensionSupported(const char *name);
 
 void VID_Shared_Init(void);
 
+void GL_InitFunctions(void);
 void GL_Setup(void);
 
 void VID_ClearExtensions(void);
@@ -223,7 +235,6 @@ qbool VID_HasScreenKeyboardSupport(void);
 void VID_ShowKeyboard(qbool show);
 qbool VID_ShowingKeyboard(void);
 
-void VID_SetMouse (qbool fullscreengrab, qbool relative, qbool hidecursor);
 void VID_Finish (void);
 
 void VID_Restart_f(struct cmd_state_s *cmd);
@@ -242,7 +253,7 @@ typedef struct
        int pixelheight_num, pixelheight_denom;
 }
 vid_mode_t;
-vid_mode_t *VID_GetDesktopMode(void);
+vid_mode_t VID_GetDesktopMode(void);
 size_t VID_ListModes(vid_mode_t *modes, size_t maxcount);
 size_t VID_SortModes(vid_mode_t *modes, size_t count, qbool usebpp, qbool userefreshrate, qbool useaspect);
 void VID_Soft_SharedSetup(void);
index 5ebb7757cbb7c7602bdda239002434e9ae7f638d..b8621dc5e50aadf024ac3badfc334cf4e2eb5364 100644 (file)
@@ -29,41 +29,12 @@ void VID_Shutdown(void)
 {
 }
 
-#ifndef WIN32
-static void signal_handler(int sig)
-{
-       Con_Printf("Received signal %d, exiting...\n", sig);
-       Sys_Quit(1);
-}
-
-static void InitSig(void)
-{
-       signal(SIGHUP, signal_handler);
-       signal(SIGINT, signal_handler);
-       signal(SIGQUIT, signal_handler);
-       signal(SIGILL, signal_handler);
-       signal(SIGTRAP, signal_handler);
-       signal(SIGIOT, signal_handler);
-       signal(SIGBUS, signal_handler);
-       signal(SIGFPE, signal_handler);
-       signal(SIGSEGV, signal_handler);
-       signal(SIGTERM, signal_handler);
-}
-#endif
-
-void VID_SetMouse (qbool fullscreengrab, qbool relative, qbool hidecursor)
-{
-}
-
 void VID_Finish (void)
 {
 }
 
 void VID_Init(void)
 {
-#ifndef WIN32
-       InitSig(); // trap evil signals
-#endif
 }
 
 qbool VID_InitMode(viddef_mode_t *mode)
@@ -76,7 +47,7 @@ void *GL_GetProcAddress(const char *name)
        return NULL;
 }
 
-void Sys_SendKeyEvents(void)
+void Sys_SDL_HandleEvents(void)
 {
 }
 
@@ -88,11 +59,6 @@ void IN_Move(void)
 {
 }
 
-vid_mode_t *VID_GetDesktopMode(void)
-{
-       return NULL;
-}
-
 size_t VID_ListModes(vid_mode_t *modes, size_t maxcount)
 {
        return 0;
index 2fb307c52d41b65ec095bf81b7ee9f5bfed7aceb..9f921a30330b7ce266e4190ddf3547605de4e239 100644 (file)
--- a/vid_sdl.c
+++ b/vid_sdl.c
@@ -30,17 +30,20 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include <IOKit/hidsystem/IOHIDLib.h>
 #include <IOKit/hidsystem/IOHIDParameter.h>
 #include <IOKit/hidsystem/event_status_driver.h>
+#if (MAC_OS_X_VERSION_MIN_REQUIRED < 120000)
+       #define IOMainPort IOMasterPort
+#endif
 static cvar_t apple_mouse_noaccel = {CF_CLIENT | CF_ARCHIVE, "apple_mouse_noaccel", "1", "disables mouse acceleration while DarkPlaces is active"};
 static qbool vid_usingnoaccel;
 static double originalMouseSpeed = -1.0;
-io_connect_t IN_GetIOHandle(void)
+static io_connect_t IN_GetIOHandle(void)
 {
        io_connect_t iohandle = MACH_PORT_NULL;
        kern_return_t status;
        io_service_t iohidsystem = MACH_PORT_NULL;
        mach_port_t masterport;
 
-       status = IOMasterPort(MACH_PORT_NULL, &masterport);
+       status = IOMainPort(MACH_PORT_NULL, &masterport);
        if(status != KERN_SUCCESS)
                return 0;
 
@@ -56,9 +59,6 @@ io_connect_t IN_GetIOHandle(void)
 #endif
 #endif
 
-#ifdef WIN32
-#define SDL_R_RESTART
-#endif
 
 // Tell startup code that we have a client
 int cl_available = true;
@@ -69,8 +69,7 @@ static qbool vid_usingmouse = false;
 static qbool vid_usingmouse_relativeworks = false; // SDL2 workaround for unimplemented RelativeMouse mode
 static qbool vid_usinghidecursor = false;
 static qbool vid_hasfocus = false;
-static qbool vid_isfullscreen;
-static qbool vid_usingvsync = false;
+static qbool vid_wmborder_waiting, vid_wmborderless;
 static SDL_Joystick *vid_sdljoystick = NULL;
 static SDL_GameController *vid_sdlgamecontroller = NULL;
 static cvar_t joy_sdl2_trigger_deadzone = {CF_ARCHIVE | CF_CLIENT, "joy_sdl2_trigger_deadzone", "0.5", "deadzone for triggers to be registered as key presses"};
@@ -80,12 +79,9 @@ static cvar_t *steelstorm_showing_mousecursor = NULL; // detect but do not creat
 
 static int win_half_width = 50;
 static int win_half_height = 50;
-static int video_bpp;
 
 static SDL_GLContext context;
 static SDL_Window *window;
-static int window_flags;
-static vid_mode_t desktop_mode;
 
 // Input handling
 
@@ -97,7 +93,8 @@ static int MapKey( unsigned int sdlkey )
 {
        switch(sdlkey)
        {
-       default: return 0;
+       // sdlkey can be Unicode codepoint for non-ascii keys, which are valid
+       default:                      return sdlkey & SDLK_SCANCODE_MASK ? 0 : sdlkey;
 //     case SDLK_UNKNOWN:            return K_UNKNOWN;
        case SDLK_RETURN:             return K_ENTER;
        case SDLK_ESCAPE:             return K_ESCAPE;
@@ -368,20 +365,20 @@ qbool VID_ShowingKeyboard(void)
        return SDL_IsTextInputActive() != 0;
 }
 
-void VID_SetMouse(qbool fullscreengrab, qbool relative, qbool hidecursor)
+static void VID_SetMouse(qbool relative, qbool hidecursor)
 {
 #ifndef DP_MOBILETOUCH
 #ifdef MACOSX
        if(relative)
                if(vid_usingmouse && (vid_usingnoaccel != !!apple_mouse_noaccel.integer))
-                       VID_SetMouse(false, false, false); // ungrab first!
+                       VID_SetMouse(false, false); // ungrab first!
 #endif
        if (vid_usingmouse != relative)
        {
                vid_usingmouse = relative;
                cl_ignoremousemoves = 2;
                vid_usingmouse_relativeworks = SDL_SetRelativeMouseMode(relative ? SDL_TRUE : SDL_FALSE) == 0;
-//             Con_Printf("VID_SetMouse(%i, %i, %i) relativeworks = %i\n", (int)fullscreengrab, (int)relative, (int)hidecursor, (int)vid_usingmouse_relativeworks);
+//             Con_Printf("VID_SetMouse(%i, %i) relativeworks = %i\n", (int)relative, (int)hidecursor, (int)vid_usingmouse_relativeworks);
 #ifdef MACOSX
                if(relative)
                {
@@ -1013,6 +1010,8 @@ void IN_Move( void )
                in_windowmouse_y = y;
        }
 
+       //Con_Printf("Mouse position: in_mouse %f %f in_windowmouse %f %f\n", in_mouse_x, in_mouse_y, in_windowmouse_x, in_windowmouse_y);
+
        VID_BuildJoyState(&joystate);
        VID_ApplyJoyState(&joystate);
 }
@@ -1021,20 +1020,6 @@ void IN_Move( void )
 // Message Handling
 ////
 
-#ifdef SDL_R_RESTART
-static qbool sdl_needs_restart;
-static void sdl_start(void)
-{
-}
-static void sdl_shutdown(void)
-{
-       sdl_needs_restart = false;
-}
-static void sdl_newmap(void)
-{
-}
-#endif
-
 static keynum_t buttonremap[] =
 {
        K_MOUSE1,
@@ -1056,13 +1041,12 @@ static keynum_t buttonremap[] =
 };
 
 //#define DEBUGSDLEVENTS
-
-// SDL2
-void Sys_SendKeyEvents( void )
+void Sys_SDL_HandleEvents(void)
 {
        static qbool sound_active = true;
        int keycode;
        int i;
+       const char *chp;
        qbool isdown;
        Uchar unicode;
        SDL_Event event;
@@ -1168,23 +1152,49 @@ void Sys_SendKeyEvents( void )
 #endif
                                                break;
                                        case SDL_WINDOWEVENT_MOVED:
-                                               break;
-                                       case SDL_WINDOWEVENT_RESIZED:
-                                               if(vid_resizable.integer < 2)
+                                               vid.xPos = event.window.data1;
+                                               vid.yPos = event.window.data2;
+                                               // Update vid.displayindex (current monitor) as it may have changed
+                                               // SDL_GetWindowDisplayIndex() doesn't work if the window manager moves the fullscreen window, but this works:
+                                               for (i = 0; i < vid_info_displaycount.integer; ++i)
                                                {
-                                                       vid.width = event.window.data1;
-                                                       vid.height = event.window.data2;
-#ifdef SDL_R_RESTART
-                                                       // better not call R_Modules_Restart_f from here directly, as this may wreak havoc...
-                                                       // so, let's better queue it for next frame
-                                                       if(!sdl_needs_restart)
+                                                       SDL_Rect displaybounds;
+                                                       if (SDL_GetDisplayBounds(i, &displaybounds) < 0)
                                                        {
-                                                               Cbuf_AddText(cmd_local, "\nr_restart\n");
-                                                               sdl_needs_restart = true;
+                                                               Con_Printf(CON_ERROR "Error getting bounds of display %i: \"%s\"\n", i, SDL_GetError());
+                                                               return;
                                                        }
-#endif
+                                                       if (vid.xPos >= displaybounds.x && vid.xPos < displaybounds.x + displaybounds.w)
+                                                       if (vid.yPos >= displaybounds.y && vid.yPos < displaybounds.y + displaybounds.h)
+                                                       {
+                                                               vid.displayindex = i;
+                                                               break;
+                                                       }
+                                               }
+                                               // when the window manager adds/removes the border it's likely to move the SDL window
+                                               // we'll need to correct that to (re)align the xhair with the monitor
+                                               if (vid_wmborder_waiting)
+                                               {
+                                                       SDL_GetWindowBordersSize(window, &i, NULL, NULL, NULL);
+                                                       if (!i != vid_wmborderless) // border state changed
+                                                       {
+                                                               SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED_DISPLAY(vid.displayindex), SDL_WINDOWPOS_CENTERED_DISPLAY(vid.displayindex));
+                                                               SDL_GetWindowPosition(window, &vid.xPos, &vid.yPos);
+                                                               vid_wmborder_waiting = false;
+                                                       }
+                                               }
+                                               break;
+                                       case SDL_WINDOWEVENT_RESIZED: // external events only
+                                               if(vid_resizable.integer < 2)
+                                               {
+                                                       //vid.width = event.window.data1;
+                                                       //vid.height = event.window.data2;
+                                                       // get the real framebuffer size in case the platform's screen coordinates are DPI scaled
+                                                       SDL_GL_GetDrawableSize(window, &vid.width, &vid.height);
                                                }
                                                break;
+                                       case SDL_WINDOWEVENT_SIZE_CHANGED: // internal and external events
+                                               break;
                                        case SDL_WINDOWEVENT_MINIMIZED:
                                                break;
                                        case SDL_WINDOWEVENT_MAXIMIZED:
@@ -1204,9 +1214,42 @@ void Sys_SendKeyEvents( void )
                                        case SDL_WINDOWEVENT_CLOSE:
                                                host.state = host_shutdown;
                                                break;
+                                       case SDL_WINDOWEVENT_TAKE_FOCUS:
+                                               break;
+                                       case SDL_WINDOWEVENT_HIT_TEST:
+                                               break;
+                                       case SDL_WINDOWEVENT_ICCPROF_CHANGED:
+                                               break;
+                                       case SDL_WINDOWEVENT_DISPLAY_CHANGED:
+                                               // this event can't be relied on in fullscreen, see SDL_WINDOWEVENT_MOVED above
+                                               vid.displayindex = event.window.data1;
+                                               break;
                                        }
                                }
                                break;
+                       case SDL_DISPLAYEVENT: // Display hotplugging
+                               switch (event.display.event)
+                               {
+                                       case SDL_DISPLAYEVENT_CONNECTED:
+                                               Con_Printf(CON_WARN "Display %i connected: %s\n", event.display.display, SDL_GetDisplayName(event.display.display));
+#ifdef __linux__
+                                               Con_Print(CON_WARN "A vid_restart may be necessary!\n");
+#endif
+                                               Cvar_SetValueQuick(&vid_info_displaycount, SDL_GetNumVideoDisplays());
+                                               // Ideally we'd call VID_ApplyDisplaySettings_c() to try to switch to the preferred display here,
+                                               // but we may need a vid_restart first, see comments in VID_ApplyDisplaySettings_c().
+                                               break;
+                                       case SDL_DISPLAYEVENT_DISCONNECTED:
+                                               Con_Printf(CON_WARN "Display %i disconnected.\n", event.display.display);
+#ifdef __linux__
+                                               Con_Print(CON_WARN "A vid_restart may be necessary!\n");
+#endif
+                                               Cvar_SetValueQuick(&vid_info_displaycount, SDL_GetNumVideoDisplays());
+                                               break;
+                                       case SDL_DISPLAYEVENT_ORIENTATION:
+                                               break;
+                               }
+                               break;
                        case SDL_TEXTEDITING:
 #ifdef DEBUGSDLEVENTS
                                Con_DPrintf("SDL_Event: SDL_TEXTEDITING - composition = %s, cursor = %d, selection lenght = %d\n", event.edit.text, event.edit.start, event.edit.length);
@@ -1219,9 +1262,14 @@ void Sys_SendKeyEvents( void )
 #endif
                                // convert utf8 string to char
                                // NOTE: this code is supposed to run even if utf8enable is 0
-                               unicode = u8_getchar_utf8_enabled(event.text.text + (int)u8_bytelen(event.text.text, 0), NULL);
-                               Key_Event(K_TEXT, unicode, true);
-                               Key_Event(K_TEXT, unicode, false);
+                               chp = event.text.text;
+                               while (*chp != 0)
+                               {
+                                       // input the chars one by one (there can be multiple chars when e.g. using an "input method")
+                                       unicode = u8_getchar_utf8_enabled(chp, &chp);
+                                       Key_Event(K_TEXT, unicode, true);
+                                       Key_Event(K_TEXT, unicode, false);
+                               }
                                break;
                        case SDL_MOUSEMOTION:
                                break;
@@ -1281,8 +1329,10 @@ void Sys_SendKeyEvents( void )
                                break;
                }
 
+       vid_activewindow = !vid_hidden && vid_hasfocus;
+
        // enable/disable sound on focus gain/loss
-       if ((!vid_hidden && vid_activewindow) || !snd_mutewhenidle.integer)
+       if (vid_activewindow || !snd_mutewhenidle.integer)
        {
                if (!sound_active)
                {
@@ -1298,6 +1348,13 @@ void Sys_SendKeyEvents( void )
                        sound_active = false;
                }
        }
+
+       if (!vid_activewindow || key_consoleactive || scr_loading)
+               VID_SetMouse(false, false);
+       else if (key_dest == key_menu || key_dest == key_menu_grabbed)
+               VID_SetMouse(vid_mouse.integer && !in_client_mouse && !vid_touchscreen.integer, !vid_touchscreen.integer);
+       else
+               VID_SetMouse(vid_mouse.integer && !cl.csqc_wantsmousemove && cl_prydoncursor.integer <= 0 && (!cls.demoplayback || cl_demo_mousegrab.integer) && !vid_touchscreen.integer, !vid_touchscreen.integer);
 }
 
 /////////////////
@@ -1316,10 +1373,137 @@ qbool GL_ExtensionSupported(const char *name)
        return SDL_GL_ExtensionSupported(name);
 }
 
-static qbool vid_sdl_initjoysticksystem = false;
+/// Applies display settings immediately (no vid_restart required).
+static void VID_ApplyDisplaySettings_c(cvar_t *var)
+{
+       unsigned int fullscreenwanted, fullscreencurrent;
+       unsigned int displaywanted = bound(0, vid_display.integer, vid_info_displaycount.integer - 1);
+
+       if (!window)
+               return;
+       Con_DPrintf("%s: %s \"%s\"\n", __func__, var->name, var->string);
+
+       fullscreencurrent = SDL_GetWindowFlags(window) & (SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_FULLSCREEN);
+       if (vid_fullscreen.integer)
+               fullscreenwanted = vid_desktopfullscreen.integer ? SDL_WINDOW_FULLSCREEN_DESKTOP : SDL_WINDOW_FULLSCREEN;
+       else
+               fullscreenwanted = 0;
+
+       // moving to another display, changing the fullscreen mode or switching to windowed
+       if (vid.displayindex != displaywanted // SDL seems unable to move any fullscreen window to another display
+       || fullscreencurrent != fullscreenwanted) // even for desktop <-> exclusive: DESKTOP flag includes FULLSCREEN bit
+       {
+               if (SDL_SetWindowFullscreen(window, 0) < 0)
+               {
+                       Con_Printf(CON_ERROR "ERROR: can't deactivate fullscreen on display %i because %s\n", vid.displayindex, SDL_GetError());
+                       return;
+               }
+               vid.fullscreen = false;
+               Con_DPrintf("Fullscreen deactivated on display %i\n", vid.displayindex);
+       }
+
+       // switching to windowed
+       if (!fullscreenwanted)
+       {
+               int toppx;
+               SDL_SetWindowSize(window, vid.width = vid_width.integer, vid.height = vid_height.integer);
+               SDL_SetWindowResizable(window, vid_resizable.integer ? SDL_TRUE : SDL_FALSE);
+               SDL_SetWindowBordered(window, (SDL_bool)!vid_borderless.integer);
+               SDL_GetWindowBordersSize(window, &toppx, NULL, NULL, NULL);
+               vid_wmborderless = !toppx;
+               if (vid_borderless.integer != vid_wmborderless) // this is not the state we're looking for
+                       vid_wmborder_waiting = true;
+       }
+
+       // moving to another display or switching to windowed
+       if (vid.displayindex != displaywanted || !fullscreenwanted)
+       {
+//             SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED_DISPLAY(displaywanted), SDL_WINDOWPOS_CENTERED_DISPLAY(displaywanted));
+//             SDL_GetWindowPosition(window, &vid.xPos, &vid.yPos);
+
+               /* bones_was_here BUG: after SDL_DISPLAYEVENT hotplug events, on Xorg + NVIDIA,
+                * SDL_WINDOWPOS_CENTERED_DISPLAY(displaywanted) may place the window somewhere completely invisible.
+                * WORKAROUND: manual positioning seems safer: although SDL_GetDisplayBounds() may return outdated values,
+                * SDL_SetWindowPosition() always placed the window somewhere fully visible, even if it wasn't correct,
+                * when tested with SDL 2.26.5.
+                */
+               SDL_Rect displaybounds;
+               if (SDL_GetDisplayBounds(displaywanted, &displaybounds) < 0)
+               {
+                       Con_Printf(CON_ERROR "Error getting bounds of display %i: \"%s\"\n", displaywanted, SDL_GetError());
+                       return;
+               }
+               vid.xPos = displaybounds.x + 0.5 * (displaybounds.w - vid.width);
+               vid.yPos = displaybounds.y + 0.5 * (displaybounds.h - vid.height);
+               SDL_SetWindowPosition(window, vid.xPos, vid.yPos);
+
+               vid.displayindex = displaywanted;
+       }
+
+       // switching to a fullscreen mode
+       if (fullscreenwanted)
+       {
+               if (fullscreenwanted == SDL_WINDOW_FULLSCREEN)
+               {
+                       // When starting in desktop/window mode no hardware mode is set so do it now,
+                       // this also applies vid_width and vid_height changes immediately without bogus modesets.
+                       SDL_DisplayMode modewanted, modeclosest;
+                       modewanted.w = vid_width.integer;
+                       modewanted.h = vid_height.integer;
+                       modewanted.refresh_rate = vid_userefreshrate.integer ? vid_refreshrate.integer : 0;
+                       if (!SDL_GetClosestDisplayMode(vid.displayindex, &modewanted, &modeclosest))
+                       {
+                               Con_Printf(CON_ERROR "Error getting closest mode to %ix%i@%ihz for display %i: \"%s\"\n", modewanted.w, modewanted.h, modewanted.refresh_rate, vid.displayindex, SDL_GetError());
+                               return;
+                       }
+                       if (SDL_SetWindowDisplayMode(window, &modeclosest) < 0)
+                       {
+                               Con_Printf(CON_ERROR "Error setting mode %ix%i@%ihz for display %i: \"%s\"\n", modeclosest.w, modeclosest.h, modeclosest.refresh_rate, vid.displayindex, SDL_GetError());
+                               return;
+                       }
+               }
+
+               if (SDL_SetWindowFullscreen(window, fullscreenwanted) < 0)
+               {
+                       Con_Printf(CON_ERROR "ERROR: can't activate fullscreen on display %i because %s\n", vid.displayindex, SDL_GetError());
+                       return;
+               }
+               // get the real framebuffer size in case the platform's screen coordinates are DPI scaled
+               SDL_GL_GetDrawableSize(window, &vid.width, &vid.height);
+               vid.fullscreen = true;
+               Con_DPrintf("Fullscreen activated on display %i\n", vid.displayindex);
+       }
+}
+
+static void VID_SetVsync_c(cvar_t *var)
+{
+       signed char vsyncwanted = cls.timedemo ? 0 : bound(-1, vid_vsync.integer, 1);
+
+       if (!context)
+               return;
+/*
+Can't check first: on Wayland SDL_GL_GetSwapInterval() may initially return 0 when vsync is on.
+On Xorg it returns the correct value.
+       if (SDL_GL_GetSwapInterval() == vsyncwanted)
+               return;
+*/
+
+       if (SDL_GL_SetSwapInterval(vsyncwanted) >= 0)
+               Con_DPrintf("Vsync %s\n", vsyncwanted ? "activated" : "deactivated");
+       else
+               Con_Printf(CON_ERROR "ERROR: can't %s vsync because %s\n", vsyncwanted ? "activate" : "deactivate", SDL_GetError());
+}
+
+static void VID_SetHints_c(cvar_t *var)
+{
+       SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH,     vid_mouse_clickthrough.integer     ? "1" : "0");
+       SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, vid_minimize_on_focus_loss.integer ? "1" : "0");
+}
 
 void VID_Init (void)
 {
+       SDL_version version;
+
 #ifndef __IPHONEOS__
 #ifdef MACOSX
        Cvar_RegisterVariable(&apple_mouse_noaccel);
@@ -1330,16 +1514,43 @@ void VID_Init (void)
 #endif
        Cvar_RegisterVariable(&joy_sdl2_trigger_deadzone);
 
-#ifdef SDL_R_RESTART
-       R_RegisterModule("SDL", sdl_start, sdl_shutdown, sdl_newmap, NULL, NULL);
+#if defined(__linux__)
+       // exclusive fullscreen is no longer functional (and when it worked was obnoxious and not faster)
+       Cvar_SetValueQuick(&vid_desktopfullscreen, 1);
+       vid_desktopfullscreen.flags |= CF_READONLY;
+#endif
+
+       Cvar_RegisterCallback(&vid_fullscreen,             VID_ApplyDisplaySettings_c);
+       Cvar_RegisterCallback(&vid_desktopfullscreen,      VID_ApplyDisplaySettings_c);
+       Cvar_RegisterCallback(&vid_display,                VID_ApplyDisplaySettings_c);
+       Cvar_RegisterCallback(&vid_width,                  VID_ApplyDisplaySettings_c);
+       Cvar_RegisterCallback(&vid_height,                 VID_ApplyDisplaySettings_c);
+       Cvar_RegisterCallback(&vid_resizable,              VID_ApplyDisplaySettings_c);
+       Cvar_RegisterCallback(&vid_borderless,             VID_ApplyDisplaySettings_c);
+       Cvar_RegisterCallback(&vid_vsync,                  VID_SetVsync_c);
+       Cvar_RegisterCallback(&vid_mouse_clickthrough,     VID_SetHints_c);
+       Cvar_RegisterCallback(&vid_minimize_on_focus_loss, VID_SetHints_c);
+
+       // DPI scaling prevents use of the native resolution, causing blurry rendering
+       // and/or mouse cursor problems and/or incorrect render area, so we need to opt-out.
+       // Must be set before first SDL_INIT_VIDEO. Documented in SDL_hints.h.
+#ifdef WIN32
+       // make SDL coordinates == hardware pixels
+       SDL_SetHint(SDL_HINT_WINDOWS_DPI_SCALING, "0");
+       // use best available awareness mode
+       SDL_SetHint(SDL_HINT_WINDOWS_DPI_AWARENESS, "permonitorv2");
 #endif
 
        if (SDL_Init(SDL_INIT_VIDEO) < 0)
                Sys_Error ("Failed to init SDL video subsystem: %s", SDL_GetError());
-       vid_sdl_initjoysticksystem = SDL_InitSubSystem(SDL_INIT_JOYSTICK) >= 0;
-       if (!vid_sdl_initjoysticksystem)
+       if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0)
                Con_Printf(CON_ERROR "Failed to init SDL joystick subsystem: %s\n", SDL_GetError());
-       vid_isfullscreen = false;
+
+       SDL_GetVersion(&version);
+       Con_Printf("Linked against SDL version %d.%d.%d\n"
+                  "Using SDL library version %d.%d.%d\n",
+                  SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL,
+                  version.major, version.minor, version.patch);
 }
 
 static int vid_sdljoystickindex = -1;
@@ -1405,16 +1616,6 @@ void VID_EnableJoystick(qbool enable)
                Cvar_SetValueQuick(&joy_active, success ? 1 : 0);
 }
 
-static void VID_OutputVersion(void)
-{
-       SDL_version version;
-       SDL_GetVersion(&version);
-       Con_Printf(     "Linked against SDL version %d.%d.%d\n"
-                                       "Using SDL library version %d.%d.%d\n",
-                                       SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL,
-                                       version.major, version.minor, version.patch );
-}
-
 #ifdef WIN32
 static void AdjustWindowBounds(viddef_mode_t *mode, RECT *rect)
 {
@@ -1462,36 +1663,29 @@ static void AdjustWindowBounds(viddef_mode_t *mode, RECT *rect)
 }
 #endif
 
-extern cvar_t gl_info_vendor;
-extern cvar_t gl_info_renderer;
-extern cvar_t gl_info_version;
-extern cvar_t gl_info_platform;
-extern cvar_t gl_info_driver;
-
 static qbool VID_InitModeGL(viddef_mode_t *mode)
 {
        int windowflags = SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL;
-       // currently SDL_WINDOWPOS_UNDEFINED behaves exactly like SDL_WINDOWPOS_CENTERED, this might change some day
-       // https://trello.com/c/j56vUcwZ/81-centered-vs-undefined-window-position
-       int xPos = SDL_WINDOWPOS_UNDEFINED;
-       int yPos = SDL_WINDOWPOS_UNDEFINED;
        int i;
 #ifndef USE_GLES2
-       const char *drivername;
+       // SDL usually knows best
+       const char *drivername = NULL;
 #endif
 
+       // video display selection (multi-monitor)
+       Cvar_SetValueQuick(&vid_info_displaycount, SDL_GetNumVideoDisplays());
+       vid.displayindex = bound(0, vid_display.integer, vid_info_displaycount.integer - 1);
+       vid.xPos = SDL_WINDOWPOS_CENTERED_DISPLAY(vid.displayindex);
+       vid.yPos = SDL_WINDOWPOS_CENTERED_DISPLAY(vid.displayindex);
+       vid_wmborder_waiting = vid_wmborderless = false;
+
        win_half_width = mode->width>>1;
        win_half_height = mode->height>>1;
 
        if(vid_resizable.integer)
                windowflags |= SDL_WINDOW_RESIZABLE;
 
-       VID_OutputVersion();
-
 #ifndef USE_GLES2
-       // SDL usually knows best
-       drivername = NULL;
-
 // COMMANDLINEOPTION: SDL GL: -gl_driver <drivername> selects a GL driver library, default is whatever SDL recommends, useful only for 3dfxogl.dll/3dfxvgl.dll or fxmesa or similar, if you don't know what this is for, you don't need it
        i = Sys_CheckParm("-gl_driver");
        if (i && i < sys.argc - 1)
@@ -1510,41 +1704,38 @@ static qbool VID_InitModeGL(viddef_mode_t *mode)
        windowflags |= SDL_WINDOW_FULLSCREEN | SDL_WINDOW_BORDERLESS;
 #endif
 
-       vid_isfullscreen = false;
+
+       if (mode->fullscreen)
        {
-               if (mode->fullscreen) {
-                       if (vid_desktopfullscreen.integer)
-                       {
-                               vid_mode_t *m = VID_GetDesktopMode();
-                               mode->width = m->width;
-                               mode->height = m->height;
-                               windowflags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
-                       }
-                       else
-                               windowflags |= SDL_WINDOW_FULLSCREEN;
-                       vid_isfullscreen = true;
+               if (vid_desktopfullscreen.integer)
+               {
+                       vid_mode_t m = VID_GetDesktopMode();
+                       mode->width = m.width;
+                       mode->height = m.height;
+                       windowflags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
                }
-               else {
-                       if (vid_borderless.integer)
-                               windowflags |= SDL_WINDOW_BORDERLESS;
+               else
+                       windowflags |= SDL_WINDOW_FULLSCREEN;
+       }
+       else
+       {
+               if (vid_borderless.integer)
+                       windowflags |= SDL_WINDOW_BORDERLESS;
+               else
+                       vid_wmborder_waiting = true; // waiting for border to be added
 #ifdef WIN32
-                       if (vid_ignore_taskbar.integer) {
-                               xPos = SDL_WINDOWPOS_CENTERED;
-                               yPos = SDL_WINDOWPOS_CENTERED;
-                       }
-                       else {
-                               RECT rect;
-                               AdjustWindowBounds(mode, &rect);
-                               xPos = rect.left;
-                               yPos = rect.top;
-                       }
-#endif
+               if (!vid_ignore_taskbar.integer)
+               {
+                       RECT rect;
+                       AdjustWindowBounds(mode, &rect);
+                       vid.xPos = rect.left;
+                       vid.xPos = rect.top;
+                       vid_wmborder_waiting = false;
                }
+#endif
        }
-       //flags |= SDL_HWSURFACE;
 
-       if (vid_mouse_clickthrough.integer && !vid_isfullscreen)
-               SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1");
+       VID_SetHints_c(NULL);
 
        SDL_GL_SetAttribute (SDL_GL_DOUBLEBUFFER, 1);
        SDL_GL_SetAttribute (SDL_GL_RED_SIZE, 8);
@@ -1569,32 +1760,58 @@ static qbool VID_InitModeGL(viddef_mode_t *mode)
        SDL_GL_SetAttribute (SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
        SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
        SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
+       /* Requesting a Core profile and 3.2 minimum is mandatory on macOS and older Mesa drivers.
+        * It works fine on other drivers too except NVIDIA, see HACK below.
+        */
 #endif
 
        SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, (gl_debug.integer > 0 ? SDL_GL_CONTEXT_DEBUG_FLAG : 0));
 
-       video_bpp = mode->bitsperpixel;
-       window_flags = windowflags;
-       window = SDL_CreateWindow(gamename, xPos, yPos, mode->width, mode->height, windowflags);
+       window = SDL_CreateWindow(gamename, vid.xPos, vid.yPos, mode->width, mode->height, windowflags);
        if (window == NULL)
        {
                Con_Printf(CON_ERROR "Failed to set video mode to %ix%i: %s\n", mode->width, mode->height, SDL_GetError());
                VID_Shutdown();
                return false;
        }
-       SDL_GetWindowSize(window, &mode->width, &mode->height);
+       // get the real framebuffer size in case the platform's screen coordinates are DPI scaled
+       SDL_GL_GetDrawableSize(window, &mode->width, &mode->height);
+       // After using SDL_WINDOWPOS_CENTERED_DISPLAY we don't know the real position
+       SDL_GetWindowPosition(window, &vid.xPos, &vid.yPos);
+
        context = SDL_GL_CreateContext(window);
        if (context == NULL)
+               Sys_Error("Failed to initialize OpenGL context: %s\n", SDL_GetError());
+
+       GL_InitFunctions();
+
+#if !defined(USE_GLES2) && !defined(MACOSX)
+       // NVIDIA hates the Core profile and limits the version to the minimum we specified.
+       // HACK: to detect NVIDIA we first need a context, fortunately replacing it takes a few milliseconds
+       gl_vendor = (const char *)qglGetString(GL_VENDOR);
+       if (strncmp(gl_vendor, "NVIDIA", 6) == 0)
        {
-               Con_Printf(CON_ERROR "Failed to initialize OpenGL context: %s\n", SDL_GetError());
-               VID_Shutdown();
-               return false;
+               Con_DPrint("The Way It's Meant To Be Played: replacing OpenGL Core profile with Compatibility profile...\n");
+               SDL_GL_DeleteContext(context);
+               SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
+               context = SDL_GL_CreateContext(window);
+               if (context == NULL)
+                       Sys_Error("Failed to initialize OpenGL context: %s\n", SDL_GetError());
        }
+#endif
 
-       SDL_GL_SetSwapInterval(bound(-1, vid_vsync.integer, 1));
-       vid_usingvsync = (vid_vsync.integer != 0);
+       // apply vid_vsync
+       Cvar_Callback(&vid_vsync);
+
+       vid_hidden = false;
+       vid_activewindow = true;
+       vid_hasfocus = true;
+       vid_usingmouse = false;
+       vid_usinghidecursor = false;
 
-       gl_platform = "SDL";
+       // clear to black (loading plaque will be seen over this)
+       GL_Clear(GL_COLOR_BUFFER_BIT, NULL, 1.0f, 0);
+       VID_Finish(); // checks vid_hidden
 
        GL_Setup();
 
@@ -1602,40 +1819,14 @@ static qbool VID_InitModeGL(viddef_mode_t *mode)
        Cvar_SetQuick(&gl_info_vendor, gl_vendor);
        Cvar_SetQuick(&gl_info_renderer, gl_renderer);
        Cvar_SetQuick(&gl_info_version, gl_version);
-       Cvar_SetQuick(&gl_info_platform, gl_platform ? gl_platform : "");
-       Cvar_SetQuick(&gl_info_driver, gl_driver);
-
-       // LadyHavoc: report supported extensions
-       Con_DPrintf("\nQuakeC extensions for server and client:");
-       for (i = 0; vm_sv_extensions[i]; i++)
-               Con_DPrintf(" %s", vm_sv_extensions[i]);
-       Con_DPrintf("\n");
-#ifdef CONFIG_MENU
-       Con_DPrintf("\nQuakeC extensions for menu:");
-       for (i = 0; vm_m_extensions[i]; i++)
-               Con_DPrintf(" %s", vm_m_extensions[i]);
-       Con_DPrintf("\n");
-#endif
+       Cvar_SetQuick(&gl_info_driver, drivername ? drivername : "");
 
-       // clear to black (loading plaque will be seen over this)
-       GL_Clear(GL_COLOR_BUFFER_BIT, NULL, 1.0f, 0);
+       for (i = 0; i < vid_info_displaycount.integer; ++i)
+               Con_Printf("Display %i: %s\n", i, SDL_GetDisplayName(i));
 
-       vid_hidden = false;
-       vid_activewindow = false;
-       vid_hasfocus = true;
-       vid_usingmouse = false;
-       vid_usinghidecursor = false;
-               
        return true;
 }
 
-extern cvar_t gl_info_extensions;
-extern cvar_t gl_info_vendor;
-extern cvar_t gl_info_renderer;
-extern cvar_t gl_info_version;
-extern cvar_t gl_info_platform;
-extern cvar_t gl_info_driver;
-
 qbool VID_InitMode(viddef_mode_t *mode)
 {
        // GAME_STEELSTORM specific
@@ -1652,23 +1843,18 @@ qbool VID_InitMode(viddef_mode_t *mode)
 void VID_Shutdown (void)
 {
        VID_EnableJoystick(false);
-       VID_SetMouse(false, false, false);
+       VID_SetMouse(false, false);
 
+       SDL_GL_DeleteContext(context);
+       context = NULL;
        SDL_DestroyWindow(window);
        window = NULL;
 
        SDL_QuitSubSystem(SDL_INIT_VIDEO);
-
-       gl_driver[0] = 0;
-       gl_extensions = "";
-       gl_platform = "";
 }
 
 void VID_Finish (void)
 {
-       qbool vid_usevsync;
-       vid_activewindow = !vid_hidden && vid_hasfocus;
-
        VID_UpdateGamma();
 
        if (!vid_hidden)
@@ -1680,28 +1866,20 @@ void VID_Finish (void)
                        CHECKGLERROR
                        if (r_speeds.integer == 2 || gl_finish.integer)
                                GL_Finish();
-
-                       vid_usevsync = (vid_vsync.integer && !cls.timedemo);
-                       if (vid_usingvsync != vid_usevsync)
-                       {
-                               vid_usingvsync = vid_usevsync;
-                               if (SDL_GL_SetSwapInterval(vid_usevsync != 0) >= 0)
-                                       Con_DPrintf("Vsync %s\n", vid_usevsync ? "activated" : "deactivated");
-                               else
-                                       Con_DPrintf("ERROR: can't %s vsync\n", vid_usevsync ? "activate" : "deactivate");
-                       }
                        SDL_GL_SwapWindow(window);
                        break;
                }
        }
 }
 
-vid_mode_t *VID_GetDesktopMode(void)
+vid_mode_t VID_GetDesktopMode(void)
 {
        SDL_DisplayMode mode;
        int bpp;
        Uint32 rmask, gmask, bmask, amask;
-       SDL_GetDesktopDisplayMode(0, &mode);
+       vid_mode_t desktop_mode;
+
+       SDL_GetDesktopDisplayMode(vid.displayindex, &mode);
        SDL_PixelFormatEnumToMasks(mode.format, &bpp, &rmask, &gmask, &bmask, &amask);
        desktop_mode.width = mode.w;
        desktop_mode.height = mode.h;
@@ -1709,23 +1887,20 @@ vid_mode_t *VID_GetDesktopMode(void)
        desktop_mode.refreshrate = mode.refresh_rate;
        desktop_mode.pixelheight_num = 1;
        desktop_mode.pixelheight_denom = 1; // SDL does not provide this
-       // TODO check whether this actually works, or whether we do still need
-       // a read-window-size-after-entering-desktop-fullscreen hack for
-       // multiscreen setups.
-       return &desktop_mode;
+       return desktop_mode;
 }
 
 size_t VID_ListModes(vid_mode_t *modes, size_t maxcount)
 {
        size_t k = 0;
        int modenum;
-       int nummodes = SDL_GetNumDisplayModes(0);
+       int nummodes = SDL_GetNumDisplayModes(vid.displayindex);
        SDL_DisplayMode mode;
        for (modenum = 0;modenum < nummodes;modenum++)
        {
                if (k >= maxcount)
                        break;
-               if (SDL_GetDisplayMode(0, modenum, &mode))
+               if (SDL_GetDisplayMode(vid.displayindex, modenum, &mode))
                        continue;
                modes[k].width = mode.w;
                modes[k].height = mode.h;
index bc4c98552e0918f82454f61ddd25d1bf6dae3d36..381efb760b8304b477b362be2901f0adbacd836e 100644 (file)
@@ -129,7 +129,6 @@ cvar_t gl_info_vendor = {CF_CLIENT | CF_READONLY, "gl_info_vendor", "", "indicat
 cvar_t gl_info_renderer = {CF_CLIENT | CF_READONLY, "gl_info_renderer", "", "indicates graphics chip model and other information"};
 cvar_t gl_info_version = {CF_CLIENT | CF_READONLY, "gl_info_version", "", "indicates version of current renderer. begins with 1.0.0, 1.1.0, 1.2.0, 1.3.1 etc."};
 cvar_t gl_info_extensions = {CF_CLIENT | CF_READONLY, "gl_info_extensions", "", "indicates extension list found by engine, space separated."};
-cvar_t gl_info_platform = {CF_CLIENT | CF_READONLY, "gl_info_platform", "", "indicates GL platform: WGL, GLX, or AGL."};
 cvar_t gl_info_driver = {CF_CLIENT | CF_READONLY, "gl_info_driver", "", "name of driver library (opengl32.dll, libGL.so.1, or whatever)."};
 
 cvar_t vid_fullscreen = {CF_CLIENT | CF_ARCHIVE, "vid_fullscreen", "1", "use fullscreen (1) or windowed (0)"};
@@ -148,9 +147,10 @@ cvar_t vid_touchscreen_density = {CF_CLIENT, "vid_touchscreen_density", "2.0", "
 cvar_t vid_touchscreen_xdpi = {CF_CLIENT, "vid_touchscreen_xdpi", "300", "Horizontal DPI of the screen (only valid on Android currently)"};
 cvar_t vid_touchscreen_ydpi = {CF_CLIENT, "vid_touchscreen_ydpi", "300", "Vertical DPI of the screen (only valid on Android currently)"};
 
-cvar_t vid_vsync = {CF_CLIENT | CF_ARCHIVE, "vid_vsync", "0", "sync to vertical blank, prevents 'tearing' (seeing part of one frame and part of another on the screen at the same time), automatically disabled when doing timedemo benchmarks"};
+cvar_t vid_vsync = {CF_CLIENT | CF_ARCHIVE, "vid_vsync", "0", "sync to vertical blank, prevents 'tearing' (seeing part of one frame and part of another on the screen at the same time) at the cost of latency, 1 always syncs and -1 is adaptive (stops syncing if the framerate drops, unsupported by some platforms), automatically disabled when doing timedemo benchmarks"};
 cvar_t vid_mouse = {CF_CLIENT | CF_ARCHIVE, "vid_mouse", "1", "whether to use the mouse in windowed mode (fullscreen always does)"};
 cvar_t vid_mouse_clickthrough = {CF_CLIENT | CF_ARCHIVE, "vid_mouse_clickthrough", "0", "mouse behavior in windowed mode: 0 = click to focus, 1 = allow interaction even if the window is not focused (click-through behaviour, can be useful when using third-party game overlays)"};
+cvar_t vid_minimize_on_focus_loss = {CF_CLIENT | CF_ARCHIVE, "vid_minimize_on_focus_loss", "0", "whether to minimize the fullscreen window if it loses focus (such as by alt+tab)"};
 cvar_t vid_grabkeyboard = {CF_CLIENT | CF_ARCHIVE, "vid_grabkeyboard", "0", "whether to grab the keyboard when mouse is active (prevents use of volume control keys, music player keys, etc on some keyboards)"};
 cvar_t vid_minwidth = {CF_CLIENT, "vid_minwidth", "0", "minimum vid_width that is acceptable (to be set in default.cfg in mods)"};
 cvar_t vid_minheight = {CF_CLIENT, "vid_minheight", "0", "minimum vid_height that is acceptable (to be set in default.cfg in mods)"};
@@ -162,10 +162,12 @@ cvar_t vid_touchscreen = {CF_CLIENT, "vid_touchscreen", "0", "Use touchscreen-st
 cvar_t vid_touchscreen_showkeyboard = {CF_CLIENT, "vid_touchscreen_showkeyboard", "0", "shows the platform's screen keyboard for text entry, can be set by csqc or menu qc if it wants to receive text input, does nothing if the platform has no screen keyboard"};
 cvar_t vid_touchscreen_supportshowkeyboard = {CF_CLIENT | CF_READONLY, "vid_touchscreen_supportshowkeyboard", "0", "indicates if the platform supports a virtual keyboard"};
 cvar_t vid_stick_mouse = {CF_CLIENT | CF_ARCHIVE, "vid_stick_mouse", "0", "have the mouse stuck in the center of the screen" };
-cvar_t vid_resizable = {CF_CLIENT | CF_ARCHIVE, "vid_resizable", "0", "0: window not resizable, 1: resizable, 2: window can be resized but the framebuffer isn't adjusted" };
+cvar_t vid_resizable = {CF_CLIENT | CF_ARCHIVE, "vid_resizable", "1", "0: window not resizable, 1: resizable, 2: window can be resized but the framebuffer isn't adjusted" };
 cvar_t vid_desktopfullscreen = {CF_CLIENT | CF_ARCHIVE, "vid_desktopfullscreen", "1", "force desktop resolution for fullscreen; also use some OS dependent tricks for better fullscreen integration"};
+cvar_t vid_display = {CF_CLIENT | CF_ARCHIVE, "vid_display", "0", "which monitor to render on, numbered from 0 (system default)" };
+cvar_t vid_info_displaycount = {CF_CLIENT | CF_READONLY, "vid_info_displaycount", "1", "how many monitors are currently available, updated by hotplug events" };
 #ifdef WIN32
-cvar_t vid_ignore_taskbar = {CF_CLIENT | CF_ARCHIVE, "vid_ignore_taskbar", "0", "in windowed mode, prevent the Windows taskbar and window borders from affecting the size and placement of the window. it will be aligned centered and uses the unaltered vid_width/vid_height values"};
+cvar_t vid_ignore_taskbar = {CF_CLIENT | CF_ARCHIVE, "vid_ignore_taskbar", "1", "in windowed mode, prevent the Windows taskbar and window borders from affecting the size and placement of the window. it will be aligned centered and uses the unaltered vid_width/vid_height values"};
 #endif
 
 cvar_t v_gamma = {CF_CLIENT | CF_ARCHIVE, "v_gamma", "1", "inverse gamma correction value, a brightness effect that does not affect white or black, and tends to make the image grey and dull"};
@@ -191,12 +193,6 @@ const char *gl_vendor;
 const char *gl_renderer;
 // begins with 1.0.0, 1.1.0, 1.2.0, 1.2.1, 1.3.0, 1.3.1, or 1.4.0
 const char *gl_version;
-// extensions list, space separated
-const char *gl_extensions;
-// WGL, GLX, or AGL
-const char *gl_platform;
-// name of driver library (opengl32.dll, libGL.so.1, or whatever)
-char gl_driver[256];
 
 #ifndef USE_GLES2
 GLboolean (GLAPIENTRY *qglIsBuffer) (GLuint buffer);
@@ -686,20 +682,40 @@ void VID_ClearExtensions(void)
        memset(&vid.support, 0, sizeof(vid.support));
 }
 
-void GL_Setup(void)
+void GL_InitFunctions(void)
 {
-       char *s;
-       int j;
-       GLint numextensions = 0;
+#ifndef USE_GLES2
        const glfunction_t *func;
        qbool missingrequiredfuncs = false;
        static char missingfuncs[16384];
 
-#ifndef USE_GLES2
        // first fetch the function pointers for everything - after this we can begin making GL calls.
        for (func = openglfuncs; func->name != NULL; func++)
                *func->funcvariable = (void *)GL_GetProcAddress(func->name);
+
+       missingfuncs[0] = 0;
+       for (func = openglfuncs; func && func->name != NULL; func++)
+       {
+               if (!*func->funcvariable && !strcmp(func->extension, "core"))
+               {
+                       Con_DPrintf("GL context is missing required function \"%s\"!\n", func->name);
+                       missingrequiredfuncs = true;
+                       dp_strlcat(missingfuncs, " ", sizeof(missingfuncs));
+                       dp_strlcat(missingfuncs, func->name, sizeof(missingfuncs));
+               }
+       }
+
+       if (missingrequiredfuncs)
+               Sys_Error("OpenGL driver/hardware lacks required features:\n%s", missingfuncs);
 #endif
+}
+
+void GL_Setup(void)
+{
+       char *s;
+       int j;
+       GLint numextensions = 0;
+       int majorv, minorv;
 
        gl_renderer = (const char *)qglGetString(GL_RENDERER);
        gl_vendor = (const char *)qglGetString(GL_VENDOR);
@@ -710,6 +726,13 @@ void GL_Setup(void)
        Con_Printf("GL_VERSION: %s\n", gl_version);
 
 #ifndef USE_GLES2
+       qglGetIntegerv(GL_MAJOR_VERSION, &majorv);
+       qglGetIntegerv(GL_MINOR_VERSION, &minorv);
+       vid.support.glversion = 10 * majorv + minorv;
+       if (vid.support.glversion < 32)
+               // fallback, should never get here: GL context creation should have failed
+               Sys_Error("OpenGL driver/hardware supports version %i.%i but 3.2 is the minimum\n", majorv, minorv);
+
        qglGetIntegerv(GL_NUM_EXTENSIONS, &numextensions);
        Con_DPrint("GL_EXTENSIONS:\n");
        for (j = 0; j < numextensions; j++)
@@ -722,23 +745,6 @@ void GL_Setup(void)
        Con_DPrint("\n");
 #endif //USE_GLES2
 
-#ifndef USE_GLES2
-       missingfuncs[0] = 0;
-       for (func = openglfuncs; func && func->name != NULL; func++)
-       {
-               if (!*func->funcvariable && !strcmp(func->extension, "core"))
-               {
-                       Con_DPrintf("GL context is missing required function \"%s\"!\n", func->name);
-                       missingrequiredfuncs = true;
-                       strlcat(missingfuncs, " ", sizeof(missingfuncs));
-                       strlcat(missingfuncs, func->name, sizeof(missingfuncs));
-               }
-       }
-
-       if (missingrequiredfuncs)
-               Sys_Error("OpenGL driver/hardware lacks required features:\n%s", missingfuncs);
-#endif
-
        Con_DPrint("Checking OpenGL extensions...\n");
 
        // detect what GLSL version is available, to enable features like higher quality reliefmapping
@@ -774,6 +780,17 @@ void GL_Setup(void)
 // COMMANDLINEOPTION: GL: -notexturegather disables GL_ARB_texture_gather (which provides fetch4 sampling)
 // COMMANDLINEOPTION: GL: -nogldebugoutput disables GL_ARB_debug_output (which provides the gl_debug feature, if enabled)
 
+#ifdef WIN32
+       // gl_texturecompression_color is somehow broken on AMD's Windows driver,
+       // see: https://gitlab.com/xonotic/darkplaces/-/issues/228
+       // HACK: force it off (less bad than adding hacky checks to the renderer)
+       if (strncmp(gl_renderer, "AMD Radeon(TM)", 14) == 0)
+       {
+               Cvar_SetQuick(&gl_texturecompression_color, "0");
+               gl_texturecompression_color.flags |= CF_READONLY;
+       }
+#endif
+
 #ifdef GL_MAX_DRAW_BUFFERS
        qglGetIntegerv(GL_MAX_DRAW_BUFFERS, (GLint*)&vid.maxdrawbuffers);
        CHECKGLERROR
@@ -1260,7 +1277,6 @@ void VID_Shared_Init(void)
        Cvar_RegisterVariable(&gl_info_renderer);
        Cvar_RegisterVariable(&gl_info_version);
        Cvar_RegisterVariable(&gl_info_extensions);
-       Cvar_RegisterVariable(&gl_info_platform);
        Cvar_RegisterVariable(&gl_info_driver);
        Cvar_RegisterVariable(&v_gamma);
        Cvar_RegisterVariable(&v_brightness);
@@ -1297,6 +1313,7 @@ void VID_Shared_Init(void)
        Cvar_RegisterVariable(&vid_vsync);
        Cvar_RegisterVariable(&vid_mouse);
        Cvar_RegisterVariable(&vid_mouse_clickthrough);
+       Cvar_RegisterVariable(&vid_minimize_on_focus_loss);
        Cvar_RegisterVariable(&vid_grabkeyboard);
        Cvar_RegisterVariable(&vid_touchscreen);
        Cvar_RegisterVariable(&vid_touchscreen_showkeyboard);
@@ -1304,6 +1321,8 @@ void VID_Shared_Init(void)
        Cvar_RegisterVariable(&vid_stick_mouse);
        Cvar_RegisterVariable(&vid_resizable);
        Cvar_RegisterVariable(&vid_desktopfullscreen);
+       Cvar_RegisterVariable(&vid_display);
+       Cvar_RegisterVariable(&vid_info_displaycount);
 #ifdef WIN32
        Cvar_RegisterVariable(&vid_ignore_taskbar);
 #endif
@@ -1422,11 +1441,15 @@ static int VID_Mode(int fullscreen, int width, int height, int bpp, float refres
                )
                        vid.sRGB2D = vid.sRGB3D = false;
 
-               Con_Printf("Video Mode: %s %dx%dx%dx%.2fhz%s\n", mode.fullscreen ? "fullscreen" : "window", mode.width, mode.height, mode.bitsperpixel, mode.refreshrate, mode.stereobuffer ? " stereo" : "");
+               Con_Printf("Video Mode: %s %dx%dx%dx%.2fhz%s on display %i\n", mode.fullscreen ? "fullscreen" : "window", mode.width, mode.height, mode.bitsperpixel, mode.refreshrate, mode.stereobuffer ? " stereo" : "", vid.displayindex);
 
-               Cvar_SetValueQuick(&vid_fullscreen, vid.mode.fullscreen);
-               Cvar_SetValueQuick(&vid_width, vid.mode.width);
-               Cvar_SetValueQuick(&vid_height, vid.mode.height);
+               // desktopfullscreen doesn't need fallback mode saved so let cvars store windowed mode dimensions
+               if (!vid_desktopfullscreen.integer) // maybe checking SDL_WINDOW_FULLSCREEN_DESKTOP is better?
+               {
+                       Cvar_SetValueQuick(&vid_fullscreen, vid.mode.fullscreen);
+                       Cvar_SetValueQuick(&vid_width, vid.mode.width);
+                       Cvar_SetValueQuick(&vid_height, vid.mode.height);
+               }
                Cvar_SetValueQuick(&vid_bitsperpixel, vid.mode.bitsperpixel);
                Cvar_SetValueQuick(&vid_samples, vid.mode.samples);
                if(vid_userefreshrate.integer)
@@ -1445,20 +1468,6 @@ static int VID_Mode(int fullscreen, int width, int height, int bpp, float refres
                return false;
 }
 
-static void VID_OpenSystems(void)
-{
-       Key_ReleaseAll();
-       R_Modules_Start();
-       S_Startup();
-}
-
-static void VID_CloseSystems(void)
-{
-       S_Shutdown();
-       R_Modules_Shutdown();
-       Key_ReleaseAll();
-}
-
 qbool vid_commandlinecheck = true;
 extern qbool vid_opened;
 
@@ -1470,16 +1479,11 @@ void VID_Restart_f(cmd_state_t *cmd)
        if (vid_commandlinecheck)
                return;
 
-       if (!vid_opened)
-       {
-               SCR_BeginLoadingPlaque(false);
-               return;
-       }
-
        Con_Printf("VID_Restart: changing from %s %dx%dx%dbpp%s, to %s %dx%dx%dbpp%s.\n",
                vid.mode.fullscreen ? "fullscreen" : "window", vid.mode.width, vid.mode.height, vid.mode.bitsperpixel, vid.mode.fullscreen && vid.mode.userefreshrate ? va(vabuf, sizeof(vabuf), "x%.2fhz", vid.mode.refreshrate) : "",
                vid_fullscreen.integer ? "fullscreen" : "window", vid_width.integer, vid_height.integer, vid_bitsperpixel.integer, vid_fullscreen.integer && vid_userefreshrate.integer ? va(vabuf, sizeof(vabuf), "x%.2fhz", vid_refreshrate.value) : "");
-       VID_CloseSystems();
+       SCR_DeferLoadingPlaque(false);
+       R_Modules_Shutdown();
        VID_Shutdown();
        if (!VID_Mode(vid_fullscreen.integer, vid_width.integer, vid_height.integer, vid_bitsperpixel.integer, vid_refreshrate.value, vid_stereobuffer.integer))
        {
@@ -1487,7 +1491,8 @@ void VID_Restart_f(cmd_state_t *cmd)
                if (!VID_Mode(vid.mode.fullscreen, vid.mode.width, vid.mode.height, vid.mode.bitsperpixel, vid.mode.refreshrate, vid.mode.stereobuffer))
                        Sys_Error("Unable to restore to last working video mode");
        }
-       VID_OpenSystems();
+       R_Modules_Start();
+       Key_ReleaseAll();
 }
 
 const char *vidfallbacks[][2] =
@@ -1561,13 +1566,9 @@ void VID_Start(void)
                if (!success)
                        Sys_Error("Video modes failed");
        }
-       VID_OpenSystems();
-}
 
-void VID_Stop(void)
-{
-       VID_CloseSystems();
-       VID_Shutdown();
+       R_Modules_Start();
+       Key_ReleaseAll();
 }
 
 static int VID_SortModes_Compare(const void *a_, const void *b_)
diff --git a/view.c b/view.c
index f3f59b428ce6e494e8b0313e09d8b03bd47aa9bb..e8eaec7e26a7b5a81d3fa500124c1d693ee674a1 100644 (file)
--- a/view.c
+++ b/view.c
@@ -32,8 +32,8 @@ when crossing a water boudnary.
 
 */
 
-cvar_t cl_rollspeed = {CF_CLIENT, "cl_rollspeed", "200", "how much strafing is necessary to tilt the view"};
-cvar_t cl_rollangle = {CF_CLIENT, "cl_rollangle", "2.0", "how much to tilt the view when strafing"};
+cvar_t cl_rollspeed = {CF_CLIENT | CF_ARCHIVE, "cl_rollspeed", "200", "how much strafing is necessary to tilt the view"};
+cvar_t cl_rollangle = {CF_CLIENT | CF_ARCHIVE, "cl_rollangle", "2.0", "how much to tilt the view when strafing"};
 
 cvar_t cl_bob = {CF_CLIENT | CF_ARCHIVE, "cl_bob","0.02", "view bobbing amount"};
 cvar_t cl_bobcycle = {CF_CLIENT | CF_ARCHIVE, "cl_bobcycle","0.6", "view bobbing speed"};
@@ -972,7 +972,7 @@ void V_MakeViewIsometric(void)
        matrix4x4_t modifiedview;
        matrix4x4_t modify;
        vec3_t forward, left, up, org;
-       float t[4][4];
+       float t[16];
 
        r_refdef.view.useperspective = false;
        r_refdef.view.usevieworiginculling = !r_trippy.value && v_isometric_usevieworiginculling.integer;
@@ -983,23 +983,23 @@ void V_MakeViewIsometric(void)
        r_refdef.view.ortho_x = r_refdef.view.frustum_x; // used by VM_CL_R_SetView
        r_refdef.view.ortho_y = r_refdef.view.frustum_y; // used by VM_CL_R_SetView
 
-       t[0][0] = v_isometric_xx.value;
-       t[0][1] = v_isometric_xy.value;
-       t[0][2] = v_isometric_xz.value;
-       t[0][3] = 0.0f;
-       t[1][0] = v_isometric_yx.value;
-       t[1][1] = v_isometric_yy.value;
-       t[1][2] = v_isometric_yz.value;
-       t[1][3] = 0.0f;
-       t[2][0] = v_isometric_zx.value;
-       t[2][1] = v_isometric_zy.value;
-       t[2][2] = v_isometric_zz.value;
-       t[2][3] = 0.0f;
-       t[3][0] = 0.0f;
-       t[3][1] = 0.0f;
-       t[3][2] = 0.0f;
-       t[3][3] = 1.0f;
-       Matrix4x4_FromArrayFloatGL(&modify, t[0]);
+       t[0]  = v_isometric_xx.value;
+       t[1]  = v_isometric_xy.value;
+       t[2]  = v_isometric_xz.value;
+       t[3]  = 0.0f;
+       t[4]  = v_isometric_yx.value;
+       t[5]  = v_isometric_yy.value;
+       t[6]  = v_isometric_yz.value;
+       t[7]  = 0.0f;
+       t[8]  = v_isometric_zx.value;
+       t[9]  = v_isometric_zy.value;
+       t[10] = v_isometric_zz.value;
+       t[11] = 0.0f;
+       t[12] = 0.0f;
+       t[13] = 0.0f;
+       t[14] = 0.0f;
+       t[15] = 1.0f;
+       Matrix4x4_FromArrayFloatGL(&modify, t);
 
        // if the orientation is locked, extract the origin and create just a translate matrix to start with
        if (v_isometric_locked_orientation.integer)
@@ -1059,6 +1059,7 @@ void V_CalcViewBlend(void)
                supercontents = CL_PointSuperContents(vieworigin);
                if (supercontents & SUPERCONTENTS_LIQUIDSMASK)
                {
+                       cl.view_underwater = true;
                        r_refdef.frustumscale_x *= 1 - (((sin(cl.time * 4.7) + 1) * 0.015) * r_waterwarp.value);
                        r_refdef.frustumscale_y *= 1 - (((sin(cl.time * 3.0) + 1) * 0.015) * r_waterwarp.value);
                        if (supercontents & SUPERCONTENTS_LAVA)
@@ -1083,6 +1084,7 @@ void V_CalcViewBlend(void)
                }
                else
                {
+                       cl.view_underwater = false;
                        cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 0;
                        cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 0;
                        cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 0;
diff --git a/vs2012_sdl2_win32.props b/vs2012_sdl2_win32.props
deleted file mode 100644 (file)
index e5fa2c1..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>\r
-<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">\r
-  <ImportGroup Label="PropertySheets" />\r
-  <PropertyGroup Label="UserMacros" />\r
-  <PropertyGroup>\r
-    <IncludePath>C:\Program Files %28x86%29\Microsoft DirectX SDK %28June 2010%29\Include;C:\dev\SDL2-2.0\include;$(IncludePath)</IncludePath>\r
-  </PropertyGroup>\r
-  <PropertyGroup>\r
-    <LibraryPath>C:\Program Files %28x86%29\Microsoft DirectX SDK %28June 2010%29\Lib\x86;C:\dev\SDL2-2.0\lib\x86;$(LibraryPath)</LibraryPath>\r
-    <_PropertySheetDisplayName>vs2012_win32</_PropertySheetDisplayName>\r
-  </PropertyGroup>\r
-  <ItemDefinitionGroup />\r
-  <ItemGroup />\r
-</Project>
\ No newline at end of file
diff --git a/vs2012_sdl2_win64.props b/vs2012_sdl2_win64.props
deleted file mode 100644 (file)
index cfe1b95..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>\r
-<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">\r
-  <ImportGroup Label="PropertySheets" />\r
-  <PropertyGroup Label="UserMacros" />\r
-  <PropertyGroup>\r
-    <IncludePath>C:\Program Files %28x86%29\Microsoft DirectX SDK %28June 2010%29\Include;C:\dev\SDL2-2.0\include;$(IncludePath)</IncludePath>\r
-  </PropertyGroup>\r
-  <PropertyGroup>\r
-    <LibraryPath>C:\Program Files %28x86%29\Microsoft DirectX SDK %28June 2010%29\Lib\x64;C:\dev\SDL2-2.0\lib\x64;$(LibraryPath)</LibraryPath>\r
-    <_PropertySheetDisplayName>vs2012_win64</_PropertySheetDisplayName>\r
-  </PropertyGroup>\r
-  <ItemDefinitionGroup />\r
-  <ItemGroup />\r
-</Project>
\ No newline at end of file
diff --git a/world.c b/world.c
index 4f7cb48c5ed99f41cb83c4d226d2cfd251f465bd..a95330fae99a4ba0ed9dc3d138d7038a6dcb9b78 100644 (file)
--- a/world.c
+++ b/world.c
@@ -119,7 +119,7 @@ void World_SetSize(world_t *world, const char *filename, const vec3_t mins, cons
 {
        int i;
 
-       strlcpy(world->filename, filename, sizeof(world->filename));
+       dp_strlcpy(world->filename, filename, sizeof(world->filename));
        VectorCopy(mins, world->mins);
        VectorCopy(maxs, world->maxs);
        world->prog = prog;
@@ -317,13 +317,17 @@ World_LinkEdict
 
 ===============
 */
-void World_LinkEdict(world_t *world, prvm_edict_t *ent, const vec3_t mins, const vec3_t maxs)
+void World_LinkEdict(world_t *world, prvm_edict_t *ent, const vec3_t mins, const vec3_t maxs, qbool link_solid_not)
 {
        prvm_prog_t *prog = world->prog;
        // unlink from old position first
        if (ent->priv.server->areagrid[0].list.prev)
                World_UnlinkEdict(ent);
 
+       // some games don't want SOLID_NOT entities linked
+       if (!link_solid_not && PRVM_serveredictfloat(ent, solid) == SOLID_NOT)
+               return;
+
        // don't add the world
        if (ent == prog->edicts)
                return;
@@ -2729,17 +2733,17 @@ treatasbox:
        if (physics_ode_trick_fixnan.integer)
        {
                test = VectorLength2(origin) + VectorLength2(forward) + VectorLength2(left) + VectorLength2(up) + VectorLength2(velocity) + VectorLength2(spinvelocity);
-               if (VEC_IS_NAN(test))
+               if (isnan(test))
                {
                        modified = true;
                        //Con_Printf("Fixing NAN values on entity %i : .classname = \"%s\" .origin = '%f %f %f' .velocity = '%f %f %f' .axis_forward = '%f %f %f' .axis_left = '%f %f %f' .axis_up = %f %f %f' .spinvelocity = '%f %f %f'\n", PRVM_NUM_FOR_EDICT(ed), PRVM_GetString(PRVM_gameedictstring(ed, classname)), origin[0], origin[1], origin[2], velocity[0], velocity[1], velocity[2], forward[0], forward[1], forward[2], left[0], left[1], left[2], up[0], up[1], up[2], spinvelocity[0], spinvelocity[1], spinvelocity[2]);
                        if (physics_ode_trick_fixnan.integer >= 2)
                                Con_Printf("Fixing NAN values on entity %i : .classname = \"%s\" .origin = '%f %f %f' .velocity = '%f %f %f' .angles = '%f %f %f' .avelocity = '%f %f %f'\n", PRVM_NUM_FOR_EDICT(ed), PRVM_GetString(prog, PRVM_gameedictstring(ed, classname)), origin[0], origin[1], origin[2], velocity[0], velocity[1], velocity[2], angles[0], angles[1], angles[2], avelocity[0], avelocity[1], avelocity[2]);
                        test = VectorLength2(origin);
-                       if (VEC_IS_NAN(test))
+                       if (isnan(test))
                                VectorClear(origin);
                        test = VectorLength2(forward) * VectorLength2(left) * VectorLength2(up);
-                       if (VEC_IS_NAN(test))
+                       if (isnan(test))
                        {
                                VectorSet(angles, 0, 0, 0);
                                VectorSet(forward, 1, 0, 0);
@@ -2747,10 +2751,10 @@ treatasbox:
                                VectorSet(up, 0, 0, 1);
                        }
                        test = VectorLength2(velocity);
-                       if (VEC_IS_NAN(test))
+                       if (isnan(test))
                                VectorClear(velocity);
                        test = VectorLength2(spinvelocity);
-                       if (VEC_IS_NAN(test))
+                       if (isnan(test))
                        {
                                VectorClear(avelocity);
                                VectorClear(spinvelocity);
diff --git a/world.h b/world.h
index 1b60ac6c2b48cbd74db530237db1db418e98ec4d..9e0e01010aa18089ea5ac68a9e07258d0692109e 100644 (file)
--- a/world.h
+++ b/world.h
@@ -112,7 +112,7 @@ void World_PrintAreaStats(world_t *world, const char *worldname);
 void World_UnlinkEdict(struct prvm_edict_s *ent);
 
 /// Needs to be called any time an entity changes origin, mins, maxs
-void World_LinkEdict(world_t *world, struct prvm_edict_s *ent, const vec3_t mins, const vec3_t maxs);
+void World_LinkEdict(world_t *world, struct prvm_edict_s *ent, const vec3_t mins, const vec3_t maxs, qbool link_solid_not);
 
 /// \returns list of entities touching a box
 int World_EntitiesInBox(world_t *world, const vec3_t mins, const vec3_t maxs, int maxlist, struct prvm_edict_s **list);
diff --git a/zone.c b/zone.c
index 40708bd6913ed713811d61c4c8443446db09c806..5d69213c006c4dda6a624a363a7567c2bbf33560 100644 (file)
--- a/zone.c
+++ b/zone.c
@@ -497,7 +497,7 @@ void _Mem_Free(void *data, const char *filename, int fileline)
        _Mem_FreeBlock((memheader_t *)((unsigned char *) data - sizeof(memheader_t)), filename, fileline);
 }
 
-mempool_t *_Mem_AllocPool(const char *name, int flags, mempool_t *parent, const char *filename, int fileline)
+mempool_t *_Mem_AllocPool(const char *name, unsigned flags, mempool_t *parent, const char *filename, int fileline)
 {
        mempool_t *pool;
        if (developer_memorydebug.integer)
@@ -520,7 +520,7 @@ mempool_t *_Mem_AllocPool(const char *name, int flags, mempool_t *parent, const
        List_Create(&pool->chain);
        pool->totalsize = 0;
        pool->realsize = sizeof(mempool_t);
-       strlcpy (pool->name, name, sizeof (pool->name));
+       dp_strlcpy (pool->name, name, sizeof (pool->name));
        pool->parent = parent;
        pool->next = poolchain;
        poolchain = pool;
@@ -649,7 +649,7 @@ void _Mem_CheckSentinelsGlobal(const char *filename, int fileline)
 #endif
 }
 
-qbool Mem_IsAllocated(mempool_t *pool, void *data)
+qbool Mem_IsAllocated(mempool_t *pool, const void *data)
 {
        memheader_t *header;
        memheader_t *target;
@@ -813,7 +813,7 @@ void Mem_PrintStats(void)
        {
                if ((pool->flags & POOLFLAG_TEMP) && !List_Is_Empty(&pool->chain))
                {
-                       Con_Printf("Memory pool %p has sprung a leak totalling %lu bytes (%.3fMB)!  Listing contents...\n", (void *)pool, (unsigned long)pool->totalsize, pool->totalsize / 1048576.0);
+                       Con_Printf(CON_WARN "Memory pool %p has sprung a leak totalling %lu bytes (%.3fMB)!  Listing contents...\n", (void *)pool, (unsigned long)pool->totalsize, pool->totalsize / 1048576.0);
                        List_For_Each_Entry(mem, &pool->chain, memheader_t, list)
                                Con_Printf("%10lu bytes allocated at %s:%i\n", (unsigned long)mem->size, mem->filename, mem->fileline);
                }
@@ -862,15 +862,15 @@ static void MemStats_f(cmd_state_t *cmd)
 }
 
 
-char* _Mem_strdup (mempool_t *pool, const char* s, const char *filename, int fileline)
+char *_Mem_strdup(mempool_t *pool, const char *s, const char *filename, int fileline)
 {
        char* p;
        size_t sz;
        if (s == NULL)
                return NULL;
        sz = strlen (s) + 1;
-       p = (char*)_Mem_Alloc (pool, NULL, sz, 16, filename, fileline);
-       strlcpy (p, s, sz);
+       p = (char*)_Mem_Alloc (pool, NULL, sz, alignof(char), filename, fileline);
+       dp_strlcpy (p, s, sz);
        return p;
 }
 
diff --git a/zone.h b/zone.h
index 3dec0b62b961e3d4e4da026721d753588554ccfe..2225ec7c7f47ea9275a9ad72b2f85a2d14dbfeb8 100644 (file)
--- a/zone.h
+++ b/zone.h
@@ -22,10 +22,17 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #define ZONE_H
 
 #include <stddef.h>
+#include <stdalign.h>
 #include "qtypes.h"
 #include "qdefs.h"
 #include "com_list.h"
 
+// Work around incomplete C11 support in Microsoft's stddef.h
+// This matches the Clang 14 header. Microsoft's double and long double are the same.
+#if defined(_MSC_VER)
+       typedef double max_align_t;
+#endif
+
 extern qbool mem_bigendian;
 
 // div0: heap overflow detection paranoia
@@ -61,7 +68,7 @@ typedef struct mempool_s
        // chain of individual memory allocations
        struct llist_s chain;
        // POOLFLAG_*
-       int flags;
+       unsigned flags;
        // total memory allocated in this pool (inside memheaders)
        size_t totalsize;
        // total memory allocated in this pool (actual malloc total)
@@ -82,9 +89,10 @@ typedef struct mempool_s
 }
 mempool_t;
 
-#define Mem_Alloc(pool,size) _Mem_Alloc(pool, NULL, size, 16, __FILE__, __LINE__)
-#define Mem_Memalign(pool,alignment,size) _Mem_Alloc(pool, NULL, size, alignment, __FILE__, __LINE__)
-#define Mem_Realloc(pool,data,size) _Mem_Alloc(pool, data, size, 16, __FILE__, __LINE__)
+#define Mem_Alloc(pool,size) _Mem_Alloc(pool, NULL, size, alignof(max_align_t), __FILE__, __LINE__)
+#define Mem_AllocType(pool,type,size) (type *)_Mem_Alloc(pool, NULL, size, alignof(type), __FILE__, __LINE__)
+#define Mem_Realloc(pool,data,size) _Mem_Alloc(pool, data, size, alignof(max_align_t), __FILE__, __LINE__)
+#define Mem_ReallocType(pool,data,type,size) (type *)_Mem_Alloc(pool, data, size, alignof(type), __FILE__, __LINE__)
 #define Mem_Free(mem) _Mem_Free(mem, __FILE__, __LINE__)
 #define Mem_strdup(pool, s) _Mem_strdup(pool, s, __FILE__, __LINE__)
 #define Mem_CheckSentinels(data) _Mem_CheckSentinels(data, __FILE__, __LINE__)
@@ -99,15 +107,15 @@ mempool_t;
 
 void *_Mem_Alloc(mempool_t *pool, void *data, size_t size, size_t alignment, const char *filename, int fileline);
 void _Mem_Free(void *data, const char *filename, int fileline);
-mempool_t *_Mem_AllocPool(const char *name, int flags, mempool_t *parent, const char *filename, int fileline);
+mempool_t *_Mem_AllocPool(const char *name, unsigned flags, mempool_t *parent, const char *filename, int fileline);
 void _Mem_FreePool(mempool_t **pool, const char *filename, int fileline);
 void _Mem_EmptyPool(mempool_t *pool, const char *filename, int fileline);
 void _Mem_CheckSentinels(void *data, const char *filename, int fileline);
 void _Mem_CheckSentinelsGlobal(const char *filename, int fileline);
 // if pool is NULL this searches ALL pools for the allocation
-qbool Mem_IsAllocated(mempool_t *pool, void *data);
+qbool Mem_IsAllocated(mempool_t *pool, const void *data);
 
-char* _Mem_strdup (mempool_t *pool, const char* s, const char *filename, int fileline);
+char *_Mem_strdup(mempool_t *pool, const char *s, const char *filename, int fileline);
 
 typedef struct memexpandablearray_array_s
 {